diff --git a/src/pentesting-web/dependency-confusion.md b/src/pentesting-web/dependency-confusion.md
index a2287183f..b692812b1 100644
--- a/src/pentesting-web/dependency-confusion.md
+++ b/src/pentesting-web/dependency-confusion.md
@@ -5,44 +5,284 @@
## Basic Information
-In summary, a dependency confusion vulnerability occurs when a project is using a library with a **misspelled** name, **inexistent** or with an **unspecified version** and the used dependency repository allows to **gather updated versions from public** repositories.
+Dependency Confusion (a.k.a. substitution attacks) happens when a package manager resolves a dependency name from an unintended, less-trusted registry/source (usually a public registry) instead of the intended private/internal one. This typically leads to the installation of an attacker-controlled package.
+
+Common root causes:
+- Typosquatting/misspelling: Importing `reqests` instead of `requests` (resolves from public registry).
+- Non-existent/abandoned internal package: Importing `company-logging` that no longer exists internally, so the resolver looks in public registries and finds an attacker’s package.
+- Version preference across multiple registries: Importing an internal `company-requests` while the resolver is allowed to also query public registries and prefers the “best”/newer version published publicly by an attacker.
+
+Key idea: If the resolver can see multiple registries for the same package name and is allowed to pick the “best” candidate globally, you’re vulnerable unless you constrain resolution.
-- **Misspelled**: Import **`reqests`** instead of `requests`
-- **Inexistent**: Import `company-logging`, an internal library which **no longer exists**
-- **Unspecified version**: Import an **internal** **existent** `company-requests` library , but the repo check **public repos** to see if there are **greater versions**.
## Exploitation
> [!WARNING]
-> In all cases the attacker just need to publish a **malicious package with name** of libraries used by the victim company.
+> In all cases, the attacker only needs to publish a malicious package with the same name as the dependency your build resolves from a public registry. Installation-time hooks (e.g., npm scripts) or import-time code paths often give code execution.
### Misspelled & Inexistent
-If your company is trying to **import a library that isn't internal**, highly probably the repo of libraries is going to be searching for it in **public repositories**. If an attacker has created it, your code and machines running is highly probably going to be compromised.
+If your project references a library that isn’t available in the private registry, and your tooling falls back to a public registry, an attacker can seed a malicious package with that name in the public registry. Your runners/CI/dev machines will fetch and execute it.
-### Unspecified Version
+### Unspecified Version / “Best-version” selection across indexes
+
+Developers frequently leave versions unpinned or allow wide ranges. When a resolver is configured with both internal and public indexes, it may select the newest version regardless of source. For internal names like `requests-company`, if the internal index has `1.0.1` but an attacker publishes `1.0.2` to the public registry and your resolver considers both, the public package may win.
-It's very common for developers to **not specify any version** of the library used, or specify just a **major version**. Then, the interpreter will try to download the **latest version** fitting those requirements.\
-If the library is a **known external library** (like python `requests`), an **attacker cannot do much**, as he won't be able to create a library called `requests` (unless he is the original author).\
-However, if the library is **internal**, like `requests-company` in this example, if the **library repo** allows to **check for new versions also externally**, it will search for a newer version publicly available.\
-So if an **attacker knows** that the company is using the `requests-company` library **version 1.0.1** (allow minor updates). He can **publish** the library `requests-company` **version 1.0.2** and the company will **use that library instead** of the internal one.
## AWS Fix
-This vulnerability was found in AWS **CodeArtifact** (read the [**details in this blog post**](https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d)).\
-AWS fixed this by allowing to specify if a library is internal or external, to avoid downloading internal dependencied from external repositories.
+This vulnerability was found in AWS CodeArtifact (read the details in this blog post). AWS added controls to mark dependencies/feeds as internal vs external so the client won’t fetch “internal” names from upstream public registries.
+
## Finding Vulnerable Libraries
-In the [**original post about dependency confusion**](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610) the author searched for thousands of exposed package.json files containing javascript project’s dependencies.
+In the original post about dependency confusion the author looked for thousands of exposed manifests (e.g., `package.json`, `requirements.txt`, lockfiles) to infer internal package names and then published higher-versioned packages to public registries.
+
+
+## Practical Attacker Playbook (for red teams in authorized tests)
+
+- Enumerate names:
+ - Grep repos and CI configs for manifest/lock files and internal namespaces.
+ - Look for organization-specific prefixes (e.g., `@company/*`, `company-*`, internal groupIds, NuGet ID patterns, private module paths for Go, etc.).
+- Check public registries for availability:
+ - If the name is unregistered publicly, register it; if it exists, attempt subdependency hijacking by targeting internal transitive names.
+- Publish with precedence:
+ - Choose a semver that “wins” (e.g., a very high version) or matches resolver rules.
+ - Include minimal install-time execution where applicable (e.g., npm `preinstall`/`install`/`postinstall` scripts). For Python, prefer import-time execution paths, as wheels typically don’t execute arbitrary code on install.
+- Exfil control:
+ - Ensure outbound is allowed from CI to your controlled endpoint; otherwise use DNS queries or error messages as a side-channel to prove code execution.
+
+> [!CAUTION]
+> Always get written authorization, use unique package names/versions for the engagement, and immediately unpublish or coordinate cleanup when testing concludes.
+
+
+## Defender Playbook (what actually prevents confusion)
+
+High-level strategies that work across ecosystems:
+- Use unique internal namespaces and bind them to a single registry.
+- Avoid mixing trust levels at resolution time. Prefer a single internal registry that proxies approved public packages instead of giving package managers both internal and public endpoints.
+- For managers that support it, map packages to specific sources (no global “best-version” across registries).
+- Pin and lock:
+ - Use lockfiles that record the resolved registry URLs (npm/yarn/pnpm) or use hash/attestation pinning (pip `--require-hashes`, Gradle dependency verification).
+- Block public fallback for internal names at the registry/network layer.
+- Reserve your internal names in public registries when feasible to prevent future squat.
+
+
+## Ecosystem Notes and Secure Config Snippets
+
+Below are pragmatic, minimal configs to reduce or eliminate dependency confusion. Prefer enforcing these in CI and developer environments.
+
+### JavaScript/TypeScript (npm, Yarn, pnpm)
+
+- Use scoped packages for all internal code and pin the scope to your private registry.
+- Keep installs immutable in CI (npm lockfile, `yarn install --immutable`).
+
+.npmrc (project-level)
+```
+# 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 (for internal package)
+```
+{
+ "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
+```
+
+Operational tips:
+- Only publish internal packages within the `@company` scope.
+- For third-party packages, allow public registry via your private proxy/mirror, not directly from clients.
+- Consider enabling npm package provenance for public packages you publish to increase traceability (doesn’t by itself prevent confusion).
+
+### Python (pip / Poetry)
+
+Core rule: Don’t use `--extra-index-url` to mix trust levels. Either:
+- Expose a single internal index that proxies and caches approved PyPI packages, or
+- Use explicit index selection and hash pinning.
+
+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
+```
+
+Generate hashed requirements with pip-tools:
+```
+# From pyproject.toml or requirements.in
+pip-compile --generate-hashes -o requirements.txt
+pip install --require-hashes -r requirements.txt
+```
+
+If you must reach public PyPI, do it via your internal proxy and maintain an explicit allowlist there. Avoid `--extra-index-url` in CI.
+
+### .NET (NuGet)
+
+Use Package Source Mapping to tie package ID patterns to explicit sources and prevent resolution from unexpected feeds.
+
+nuget.config
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Java (Maven/Gradle)
+
+Maven settings.xml (mirror all to internal; disallow ad-hoc repos in POMs via Enforcer):
+```
+
+
+
+ internal-mirror
+ *
+ https://maven.corp.example/repository/group
+
+
+
+```
+
+Add Enforcer to ban repositories declared in POMs and force usage of your mirror:
+```
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.6.1
+
+
+ enforce-no-repositories
+ enforce
+
+
+
+
+
+
+
+
+```
+
+Gradle: Centralize and lock dependencies.
+- Enforce repositories in `settings.gradle(.kts)` only:
+```
+dependencyResolutionManagement {
+ repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
+ repositories {
+ maven { url = uri("https://maven.corp.example/repository/group") }
+ }
+}
+```
+- Enable dependency verification (checksums/signatures) and commit `gradle/verification-metadata.xml`.
+
+### Go Modules
+
+Configure private modules so the public proxy and checksum DB aren’t used for them.
+
+```
+# 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)
+
+Replace crates.io with an approved internal mirror or vendor directory for builds; do not allow arbitrary public fallback.
+
+.cargo/config.toml
+```
+[source.crates-io]
+replace-with = "corp-mirror"
+
+[source.corp-mirror]
+registry = "https://crates-mirror.corp.example/index"
+```
+
+For publishing, be explicit with `--registry` and keep credentials scoped to the target registry.
+
+### Ruby (Bundler)
+
+Use source blocks and disable multisource Gemfiles so gems come only from the intended repository.
+
+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
+```
+
+Enforce at config level:
+```
+bundle config set disable_multisource true
+```
+
+
+## CI/CD and Registry Controls That Help
+
+- Private registry as a single ingress:
+ - Use Artifactory/Nexus/CodeArtifact/GitHub Packages/Azure Artifacts as the only endpoint developers/CI can reach.
+ - Implement block/allow rules so internal namespaces never resolve from upstream public sources.
+- Lockfiles are immutable in CI:
+ - npm: commit `package-lock.json`, use `npm ci`.
+ - Yarn: commit `yarn.lock`, use `yarn install --immutable`.
+ - Python: commit hashed `requirements.txt`, enforce `--require-hashes`.
+ - Gradle: commit `verification-metadata.xml` and fail on unknown artifacts.
+- Outbound egress control: block direct access from CI to public registries except via the approved proxy.
+- Name reservation: pre-register your internal names/namespaces in public registries where supported.
+- Package provenance / attestations: when publishing public packages, enable provenance/attestations to make tampering more detectable downstream.
+
## References
- [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}}
-
-
-