供应链攻击取证深度分析

供应链攻击取证深度分析

软件供应链攻击是近年来增长最快、影响最深远的安全威胁之一。与传统的漏洞利用不同,攻击者不再逐个突破目标系统,而是通过污染软件的生产、分发和更新环节,将恶意代码植入受害者"主动信任"的渠道中。从 2020 年的 SolarWinds/SUNBURST 事件到 2024 年的 XZ Utils 后门,供应链攻击的规模和技术复杂度持续攀升。

对于取证分析人员而言,供应链攻击带来了独特挑战:恶意代码寄生在合法软件中,传统的恶意软件特征匹配难以奏效;攻击链横跨多个可信实体(开发者、仓库、构建系统、更新服务器),需要跨域取证;持久化机制隐匿于软件依赖关系中,难以通过常规排查手段发现。

本文系统性地梳理软件供应链攻击的取证分析方法论,从依赖投毒到 CI/CD 流水线污染,从代码签名链验证到固件级篡改,结合 SolarWinds、XZ Utils、event-stream 等真实案例还原完整攻击链,并提供可直接落地的自动化检测脚本和 Sigma 规则。


0x01 技术基础与供应链攻击取证概述

供应链攻击分类体系

供应链攻击(Supply Chain Attack,MITRE ATT&CK T1195)涵盖软件从开发到交付全生命周期的多个环节。根据攻击介入点的不同,可以将供应链攻击划分为四大类别。

攻击类别攻击介入点典型手法代表案例
依赖投毒包仓库(npm/PyPI/Maven)Typosquatting、Dependency Confusion、Protestwareevent-stream、ua-parser-js
构建环境入侵CI/CD 流水线Workflow 篡改、Secrets 窃取、恶意 Action 注入Codecov、3CX
更新机制劫持自动更新通道DLL 侧加载、代码签名伪造、OTA 劫持SolarWinds、CCleaner
硬件/固件供应链UEFI/驱动/硬件固件篡改、驱动签名伪造、硬件植入MosaicRegressor、BlackLotus

与传统恶意软件攻击的差异

供应链攻击与传统恶意软件攻击在多个维度上存在本质差异,这些差异直接影响取证分析的方法论选择。

对比维度传统恶意软件攻击供应链攻击
信任链利用无,直接利用漏洞或社会工程污染受害者已信任的软件来源
检测难度签名/沙箱检测相对成熟恶意代码寄生在合法软件中,检测率低
影响范围逐台感染,速度受限通过更新机制一次性影响大规模用户
取证重点单台主机的行为分析跨实体的依赖关系和构建链审计
时间窗口攻击时间明确投毒到发现的潜伏期可长达数月
IOC 类型恶意文件哈希、C2 IP恶意包版本号、构建哈希、可疑 workflow 变更

取证工具链

供应链攻击取证需要一套专门化的工具链,覆盖依赖审计、漏洞匹配、签名验证和构建溯源等多个环节。

工具名称功能定位适用场景安装方式
npm auditnpm 依赖漏洞扫描JavaScript 项目依赖审计内置 npm CLI
pip-auditPython 依赖漏洞扫描Python 项目依赖审计pip install pip-audit
OWASP Dependency-Check多语言依赖漏洞扫描Java/C#/Ruby 等多语言项目CLI 或 Maven/Gradle 插件
SyftSBOM(软件物料清单)生成生成 SPDX/CycloneDX 格式 SBOMCLI 二进制
GrypeSBOM 漏洞匹配基于 SBOM 的漏洞扫描CLI 二进制
Cosign容器/二进制签名验证Sigstore 生态签名验证CLI 二进制
in-toto供应链完整性验证软件供应链元数据验证Python/Go
Sigstore透明日志与签名代码签名、透明日志查询多语言 SDK
Rekor供应链透明日志查询签名记录和审计轨迹CLI / API
CHIPSEC固件/UEFI 安全检查BIOS/UEFI 篡改检测Python 框架
UEFIToolUEFI 固件解析固件镜像分析和提取Java 应用
syft + grypeSBOM 生成 + 漏洞匹配组合容器镜像供应链审计CLI 组合使用

0x02 软件依赖投毒取证

npm/PyPI/Maven 仓库投毒机制

依赖投毒是供应链攻击中最常见、门槛最低的攻击方式。攻击者利用开发者对公共仓库的信任,通过多种手段将恶意代码注入到合法的依赖关系链中。

投毒机制MITRE ATT&CK攻击原理防御难点
TyposquattingT1195.002注册与流行包名拼写相似的恶意包开发者输入错误难以完全避免
Dependency ConfusionT1195.002利用包管理器的命名空间优先级差异,用同名私有包覆盖混淆公共/私有仓库优先级
ProtestwareT1195.002在合法包中植入政治抗议代码,如 node-ipc道德边界模糊,检测困难
Star-jackingT1195.002购买或窃取高星仓库,替换为恶意代码利用开发者对星数的盲目信任
Maintainer TakeoverT1195.002入侵维护者账号后发布恶意版本需要多因素认证保护
Namespace SquattingT1195.002在新命名空间中抢注流行包的 scope 名需要仓库平台的主动治理

package.json/requirements.xml 篡改检测

在取证分析中,需要重点检查依赖声明文件是否被篡改。以下是各生态的检查要点。

npm 生态检查:

git log --oneline -20 -- package.json package-lock.json
git diff HEAD~10 -- package.json package-lock.json

Python 生态检查:

git log --oneline -20 -- requirements.txt requirements.lock setup.py pyproject.toml
pip-audit --require-hashes -r requirements.txt

Java 生态检查:

git log --oneline -20 -- pom.xml build.gradle
mvn dependency:tree -DoutputType=dot
检查项npmPythonJava
依赖声明文件package.jsonrequirements.txt / pyproject.tomlpom.xml / build.gradle
锁定文件package-lock.jsonrequirements.lock / Pipfile.lock无标准锁文件
依赖树命令npm ls –allpip show / pipdeptreemvn dependency:tree
漏洞扫描npm auditpip-auditOWASP Dependency-Check
私有仓库配置.npmrcpip.conf / .pypircsettings.xml

lockfile 取证与依赖树还原

锁文件(Lockfile)是供应链取证的关键证据来源。它记录了每次安装时实际使用的依赖版本和完整性哈希。

npm lockfile 分析:

cat package-lock.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
packages = data.get('packages', data.get('dependencies', {}))
for name, info in packages.items():
    version = info.get('version', 'unknown')
    resolved = info.get('resolved', 'N/A')
    integrity = info.get('integrity', 'N/A')
    print(f'{name} => {version}')
    print(f'  Resolved: {resolved}')
    print(f'  Integrity: {integrity[:40]}...')
"

Python lockfile 比对:

diff <(pip freeze | sort) requirements-lock.txt
pip-audit --fix --dry-run

npm audit / pip-audit / OWASP Dependency-Check 实战

npm audit --json | python3 -c "
import json, sys
data = json.load(sys.stdin)
vulns = data.get('vulnerabilities', {})
for name, info in vulns.items():
    severity = info.get('severity', 'unknown')
    via = info.get('via', [])
    fix_available = info.get('fixAvailable', False)
    print(f'[{severity.upper()}] {name}')
    for v in via:
        if isinstance(v, dict):
            print(f'  Title: {v.get(\"title\", \"N/A\")}')
            print(f'  URL: {v.get(\"url\", \"N/A\")}')
    print(f'  Fix Available: {fix_available}')
    print()
"
pip-audit --format json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for dep in data.get('dependencies', []):
    name = dep.get('name', '')
    version = dep.get('version', '')
    vulns = dep.get('vulns', [])
    if vulns:
        print(f'[VULNERABLE] {name}=={version}')
        for v in vulns:
            print(f'  ID: {v.get(\"id\", \"N/A\")}')
            print(f'  Description: {v.get(\"description\", \"N/A\")[:80]}')
"

0x03 CI/CD 流水线污染取证

GitHub Actions 污染

GitHub Actions 是目前最广泛使用的 CI/CD 平台之一,也是供应链攻击的重要目标。攻击者可以通过多种方式污染 GitHub Actions 工作流。

攻击手法MITRE ATT&CK具体实现危害等级
Workflow 篡改T1195.002修改 .github/workflows/*.yml 添加恶意步骤🔴 严重
Secrets 窃取T1552.001通过恶意 Action 读取 ${{ secrets.* }} 并外传🔴 严重
恶意 Action 注入T1195.002使用 uses: evil/action@main 引入恶意 action🔴 严重
Runner 提权T1078.004利用 self-hosted runner 的持久化环境🟡 高危
缓存投毒T1195.002污染 actions/cache 中的构建缓存🟡 高危
OIDC 令牌滥用T1078.004利用 id-token 权限伪造云平台凭证🔴 严重

.github/workflows/ 变更审计

git log --all --oneline --diff-filter=ACMR -- '.github/workflows/'
for commit in $(git log --all --oneline -- '.github/workflows/' | head -10 | awk '{print $1}'); do
    echo "=== Commit: $commit ==="
    git show --stat $commit -- '.github/workflows/'
    git show $commit -- '.github/workflows/' | head -50
    echo ""
done
git log --all --oneline -- '.github/workflows/' | while read hash msg; do
    echo "--- $hash: $msg ---"
    git show $hash -- '.github/workflows/' 2>/dev/null | grep -E '^\+.*uses:|^\+.*run:|^\+.*secrets\.' | head -20
done

Jenkins/Travis CI/GitLab CI 构建环境入侵

CI/CD 平台配置文件路径高风险检查点取证命令
GitHub Actions.github/workflows/*.ymluses: 引用、secrets.* 访问git log -p -- .github/workflows/
JenkinsJenkinsfile / 构建目录脚本步骤、凭据使用查看 config.xml 和构建历史
Travis CI.travis.ymlscriptbefore_install 钩子git log -p -- .travis.yml
GitLab CI.gitlab-ci.ymlscriptservices 定义git log -p -- .gitlab-ci.yml
CircleCI.circleci/config.ymlcommandsjobs 定义git log -p -- .circleci/

构建日志与 artifact 分析

if command -v gh &> /dev/null; then
    gh run list --limit 10
    for run_id in $(gh run list --limit 5 --json databaseId -q '.[].databaseId'); do
        echo "=== Run ID: $run_id ==="
        gh run view $run_id --log 2>/dev/null | grep -iE 'upload|download|secret|token|curl|wget|eval|exec|base64|reverse' | head -20
    done
fi

0x04 开发者账号与签名链取证

GPG/PGP 签名验证

代码签名是验证软件来源真实性和完整性的关键机制。在供应链攻击中,攻击者往往需要伪造或绕过签名验证。

git log --show-signature --oneline -20
git log --format='%H %G? %GS %GK %aN <%aE>' -20
签名状态码含义取证判断
G(Good)签名有效且密钥可信正常提交
B(Bad)签名无效🟡 高度可疑,可能被篡改
U(Unknown)密钥不在信任链中🟡 需要关注,可能使用了新的密钥
X(Expired)签名密钥已过期🟡 需要关注,可能使用了过期密钥
N(Not Signed)无签名🟡 需要关注,特别是关键依赖的提交

Sigstore/Rekor 透明日志验证

Sigstore 是新一代的软件签名基础设施,提供签名、验证和透明日志记录功能。

cosign verify-blob \
    --bundle ./artifact.bundle \
    --certificate-identity=https://github.com/owner/repo/.github/workflows/build.yml@refs/heads/main \
    --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
    ./artifact
cosign verify \
    --certificate-identity=user@example.com \
    --certificate-oidc-issuer=https://accounts.google.com \
    ghcr.io/owner/image:tag
rekor-cli search --email user@example.com
rekor-cli get --log-index <log_index>
验证工具验证对象验证内容命令示例
cosign verify容器镜像签名者身份和证书链cosign verify --certificate-identity=... image
cosign verify-blob任意文件文件签名和透明日志cosign verify-blob --bundle=... file
gh attestation verifyGitHub 构建产物构建来源和完整性gh attestation verify --owner=... file
rekor-cli透明日志签名记录和审计轨迹rekor-cli get --log-index=N
in-toto-verify软件供应链步骤完整性和作者签名in-toto-verify --layout=...

SSDF 合规性检查

NIST SP 800-218 Secure Software Development Framework (SSDF) 为供应链安全提供了系统化的安全实践框架。

SSDF 实践说明检查方法
PO 1.1定义安全开发策略和实践审查组织的安全开发策略文档
PS 1.1保护所有形式的代码免受未授权修改检查分支保护规则和签名要求
PS 1.2保护代码仓库免受未授权访问审查访问控制和审计日志
PW 1.1使用自动化工具验证代码质量和安全性检查 CI/CD 中的安全扫描步骤
PW 8.1通过维护者审查确保代码质量审查 PR 审查流程和要求
PW 8.2使用自动化工具验证软件质量检查自动化测试覆盖率
RV 1.1验证软件符合安全要求检查发布前的安全审查流程

GitHub 2FA 状态与 Token 泄露检测

if command -v gh &> /dev/null; then
    gh api user --jq '{login: .login, two_factor_enabled: .two_factor_enabled, created_at: .created_at}'
fi
gh api user/tokens --jq '.[] | {id: .id, name: .name, scopes: .scopes, created_at: .created_at, last_used_at: .last_used_at}' 2>/dev/null

0x05 SolarWinds 式高级持续性供应链攻击取证

DLL 侧加载与 SUNBURST 后门机制

SolarWinds 攻击(2020 年)是供应链攻击的标志性事件。攻击者(APT29/Cozy Bear)通过入侵 SolarWinds 的构建系统,将 SUNBURST 后门植入 Orion 平台的合法更新包中。

阶段MITRE ATT&CK具体行为取证要点
构建入侵T1195.002修改 SolarWinds.Orion.Core.BusinessLayer.dll对比官方构建哈希
DLL 侧加载T1574.002通过 SolarWinds.dll 加载 SUNBURST检查 DLL 加载顺序和路径
域前置 C2T1090.004使用 Azure/Amazon CDN 作为 C2 中继分析网络流量和 DNS 查询
横向移动T1021.002使用窃取的 SAML 令牌检查认证日志和令牌使用
凭证窃取T1003提取 LSASS 内存中的凭证内存取证和日志分析
持久化T1546通过计划任务和服务维持访问检查系统服务和计划任务

代码注入到合法更新流程

SUNBURST 后门的隐蔽性在于它寄生在 SolarWinds Orion 的合法更新机制中。攻击者在构建阶段将恶意代码注入到 SolarWinds.Orion.Core.BusinessLayer.dll 中,使得每次合法更新都会携带后门。

strings SolarWinds.Orion.Core.BusinessLayer.dll | grep -iE 'avsvm|update|cdn|solr|json'
sha256sum SolarWinds.Orion.Core.BusinessLayer.dll

已知被感染的 SolarWinds 二进制文件哈希(部分):

文件名SHA-256 哈希(部分)说明
SolarWinds.Orion.Core.BusinessLayer.dll32507a0...SUNBURST 后门载体
SolarWinds.BusinessLayerHost.exed132b66...主进程入口
Solarwindsservice.exeb98a482...服务进程

内存取证与无文件阶段检测

SUNBURST 后门在运行期间采用无文件驻留技术,恶意代码仅存在于内存中。

volatility -f memory.dump --profile=Win10x64_18362 malfind | grep -A 20 "SolarWinds"
volatility -f memory.dump --profile=Win10x64_18362 netscan | grep -iE 'avsvm|azure|amazon'
volatility -f memory.dump --profile=Win10x64_18362 handles -p $(pgrep -f SolarWinds) | grep -iE 'File|Section|Event'

域前置 C2 通信分析

SUNBURST 后门使用域前置(Domain Fronting)技术隐藏 C2 通信。它将 C2 流量伪装成对合法 CDN 服务的请求。

import requests

c2_indicators = [
    "avsvmcloud.com",
    "freescanonline.com",
    "deftsecurity.com",
    "thedoccloud.com",
    "virtualsitting.com",
    "websitetheme.com",
    "highstechlabs.com",
    "azureonline.cloud",
    "decolourmachines.com",
]

for domain in c2_indicators:
    try:
        resp = requests.get(f"https://{domain}", timeout=10, verify=True)
        print(f"[{resp.status_code}] {domain} => {resp.headers.get('Server', 'N/A')}")
    except requests.exceptions.RequestException as e:
        print(f"[ERROR] {domain} => {str(e)[:80]}")

0x06 操作系统与固件供应链取证

UEFI/BIOS 篡改检测

UEFI 固件供应链攻击是最高级别的持久化手段。攻击者通过篡改 UEFI 固件,可以在操作系统重装甚至硬盘更换后仍然保持驻留。

攻击层级MITRE ATT&CK典型工具检测方法
UEFI BootkitT1542.001BlackLotus、MosaicRegressorCHIPSEC、UEFITool
DXE 驱动注入T1542.001ESPaigner、CosmicStrandUEFITool 模块分析
Secure Boot 绕过T1542.001BootHole (CVE-2020-1071)mokutil –sb-state
SPI Flash 篡改T1542.001自定义固件修改CHIPSEC SPI 读取
Option ROM 注入T1542.001恶意网卡/显卡 ROMROM 提取和分析

驱动签名伪造与 WinRing0/rootkit

CHIPSEC 使用示例:
chipsec_main --module common.secureboot.variables
chipsec_main --module common.winring0
chipsec_main --module common.spi
# Windows 环境下检查驱动签名
sigcheck.exe -accepteula -vt -u C:\Windows\System32\drivers\*.sys 2>/dev/null
import subprocess
import json

def check_driver_signatures(driver_path):
    result = subprocess.run(
        ["sigcheck.exe", "-accepteula", "-vt", "-u", driver_path],
        capture_output=True, text=True, timeout=30
    )
    output = result.stdout
    signed_drivers = []
    unsigned_drivers = []
    for line in output.split('\n'):
        if 'Signed' in line and 'Unsigned' not in line:
            signed_drivers.append(line.strip())
        elif 'Unsigned' in line:
            unsigned_drivers.append(line.strip())
    return {
        "signed": signed_drivers,
        "unsigned": unsigned_drivers,
        "total_signed": len(signed_drivers),
        "total_unsigned": len(unsigned_drivers),
    }

OTA 更新劫持

OTA 劫持方式MITRE ATT&CK攻击原理检测线索
MitM 替换T1557拦截并替换更新请求的响应证书固定检查、TLS 验证
DNS 劫持T1584.001将更新域名解析到恶意服务器DNS 日志、域名历史解析记录
证书伪造T1553使用伪造的代码签名证书证书透明度日志查询
更新服务器入侵T1195.002直接入侵上游更新服务器服务端日志和访问控制审计
CDN 投毒T1195.002污染 CDN 缓存的更新文件CDN 缓存日志和文件哈希

固件完整性验证

mokutil --sb-state
# Linux 下检查 Secure Boot 状态
dmesg | grep -i 'secure boot'
dmesg | grep -i 'lockdown'
# 使用 CHIPSEC 检查 Secure Boot 配置
chipsec_main --module common.secureboot.enabled
chipsec_main --module common.secureboot.setup

0x07 证据强度分层与案例关联

在供应链攻击取证中,对证据的准确分级直接影响事件响应的优先级和处置决策。以下分级框架基于证据的确凿程度和恶意意图的明确性。

🔴 确认恶意

无可争议的恶意代码证据。当出现以下证据时,可以确认发生了供应链攻击。

编号证据类型具体示例确认依据
C-1嵌入后门代码package.json 的 postinstall 脚本中包含 eval(atob('...'))明确的代码执行意图
C-2数据外传行为依赖包将环境变量 POST 到外部服务器明确的数据窃取行为
C-3凭证窃取postinstall 脚本读取 .env.npmrc.ssh/ 目录明确的凭证收集行为
C-4反向 Shell依赖安装过程中建立反向 TCP/HTTP 连接明确的远程访问意图
C-5加密货币挖矿preinstall 脚本下载并执行矿工程序明确的资源滥用行为

🟡 高度可疑

强烈暗示恶意活动但需要进一步验证的证据。

编号证据类型具体示例可疑原因
S-1异常依赖行为包安装时发起与功能无关的网络请求可能为遥测/分析,也可能为恶意
S-2非预期代码修改维护者在版本更新中添加大量混淆代码可能为性能优化,也可能为隐藏恶意代码
S-3不一致的签名包的签名者与已知维护者不符可能为新的协作者,也可能为账号泄露
S-4异常发布时间新版本在非常规时间(如凌晨)发布可能为个人习惯,也可能为被盗账号
S-5版本号跳跃版本号从 1.x.x 突然跳到 3.x.x可能为重写,也可能为 Typosquatting

🟢 需要关注

可能为正常行为但需要结合上下文判断的证据。

编号证据类型具体示例关注原因
W-1依赖版本漂移lockfile 中的间接依赖版本发生变化可能为正常更新,也可能为依赖混淆
W-2非活跃维护包依赖包超过 2 年未更新无维护的包更容易被接管
W-3可选依赖变更optionalDependencies 中新增了不常见的包可能为功能扩展,也可能为投毒尝试
W-4安装脚本变更scripts 字段中的钩子函数发生变化可能为构建优化,也可能为恶意注入
W-5新增 scope 包新增了 @unknown-scope/* 的依赖可能为正常模块化,也可能为命名空间劫持

证据关联分析方法

python3 << 'PYTHON'
import json
import os
import hashlib

def analyze_supply_chain_evidence(project_dir):
    evidence = {
        "critical": [],
        "suspicious": [],
        "watch": []
    }

    pkg_json_path = os.path.join(project_dir, "package.json")
    if os.path.exists(pkg_json_path):
        with open(pkg_json_path) as f:
            pkg = json.load(f)

        scripts = pkg.get("scripts", {})
        for hook in ["preinstall", "postinstall", "prepare", "prepublish"]:
            if hook in scripts:
                cmd = scripts[hook]
                if any(keyword in cmd for keyword in ["eval", "exec", "curl", "wget", "base64", "child_process"]):
                    evidence["critical"].append({
                        "type": "MALICIOUS_SCRIPT",
                        "hook": hook,
                        "command": cmd,
                        "severity": "CRITICAL",
                        "mitre": "T1195.002"
                    })
                elif any(keyword in cmd for keyword in ["http", "https", "request", "fetch"]):
                    evidence["suspicious"].append({
                        "type": "SUSPICIOUS_NETWORK",
                        "hook": hook,
                        "command": cmd,
                        "severity": "HIGH",
                        "mitre": "T1195.002"
                    })

        deps = {**pkg.get("dependencies", {}), **pkg.get("devDependencies", {})}
        for name, version in deps.items():
            if version.startswith("http") or version.startswith("git+"):
                evidence["suspicious"].append({
                    "type": "URL_DEPENDENCY",
                    "package": name,
                    "version": version,
                    "severity": "HIGH",
                    "mitre": "T1195.002"
                })

    return evidence

if __name__ == "__main__":
    import sys
    target = sys.argv[1] if len(sys.argv) > 1 else "."
    result = analyze_supply_chain_evidence(target)
    print(json.dumps(result, indent=2, ensure_ascii=False))
PYTHON

0x08 自动化检测与狩猎

Sigma 规则

以下 Sigma 规则用于检测供应链攻击中的关键行为。

title: Suspicious npm install Network Activity
id: 5a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d
status: experimental
description: Detects suspicious network requests during npm package installation
references:
  - https://attack.mitre.org/techniques/T1195/002/
author: x7peeps
date: 2026/07/05
tags:
  - attack.supply_chain
  - attack.t1195.002
logsource:
  category: process_creation
  product: windows
detection:
  selection_npm_install:
    CommandLine|contains|all:
      - 'npm'
      - 'install'
  selection_suspicious_command:
    CommandLine|contains:
      - 'curl'
      - 'wget'
      - 'Invoke-WebRequest'
      - 'Net.WebClient'
      - 'base64'
      - 'eval'
      - 'exec'
  condition: selection_npm_install and selection_suspicious_command
level: high
title: GitHub Actions Workflow Modified
id: 6b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6e
status: experimental
description: Detects modifications to GitHub Actions workflow files
references:
  - https://attack.mitre.org/techniques/T1195/002/
author: x7peeps
date: 2026/07/05
tags:
  - attack.supply_chain
  - attack.t1195.002
logsource:
  category: file_event
  product: linux
detection:
  selection_workflow_create:
    TargetFilename|contains|all:
      - '.github/workflows/'
      - '.yml'
  selection_workflow_modify:
    TargetFilename|contains|all:
      - '.github/workflows/'
  filter_legitimate:
    TargetFilename|endswith:
      - '.github/workflows/ci.yml'
  condition: selection_workflow_create or (selection_workflow_modify and not filter_legitimate)
level: medium

Bash 脚本:自动化扫描

#!/bin/bash
SCAN_DIR="${1:-.}"
REPORT_FILE="supply_chain_audit_$(date +%Y%m%d_%H%M%S).txt"

echo "========================================" | tee "$REPORT_FILE"
echo " Supply Chain Security Audit Report" | tee -a "$REPORT_FILE"
echo " Scan Directory: $SCAN_DIR" | tee -a "$REPORT_FILE"
echo " Scan Time: $(date)" | tee -a "$REPORT_FILE"
echo "========================================" | tee -a "$REPORT_FILE"

echo "" | tee -a "$REPORT_FILE"
echo "[Phase 1] Checking package.json for suspicious scripts..." | tee -a "$REPORT_FILE"
find "$SCAN_DIR" -name "package.json" -not -path "*/node_modules/*" | while read pkgfile; do
    echo "File: $pkgfile" | tee -a "$REPORT_FILE"
    python3 -c "
import json, sys
with open('$pkgfile') as f:
    data = json.load(f)
scripts = data.get('scripts', {})
for hook, cmd in scripts.items():
    if any(x in cmd for x in ['eval', 'exec', 'curl', 'wget', 'base64', 'child_process', 'http://', 'https://']):
        print(f'  [CRITICAL] {hook}: {cmd}')
    elif any(x in cmd for x in ['require', 'import', 'spawn']):
        print(f'  [SUSPICIOUS] {hook}: {cmd}')
" 2>/dev/null | tee -a "$REPORT_FILE"
done

echo "" | tee -a "$REPORT_FILE"
echo "[Phase 2] Checking for URL-based dependencies..." | tee -a "$REPORT_FILE"
find "$SCAN_DIR" -name "package.json" -not -path "*/node_modules/*" | while read pkgfile; do
    grep -E '"(http|git\+|git://|ssh://)' "$pkgfile" | while read line; do
        echo "  [SUSPICIOUS] $pkgfile: $line" | tee -a "$REPORT_FILE"
    done
done

echo "" | tee -a "$REPORT_FILE"
echo "[Phase 3] Checking for hidden GitHub workflows..." | tee -a "$REPORT_FILE"
find "$SCAN_DIR" -path "*/.github/workflows/*.yml" -o -path "*/.github/workflows/*.yaml" | while read wf; do
    echo "  Workflow: $wf" | tee -a "$REPORT_FILE"
    grep -n "uses:" "$wf" | grep -vE "actions/|github/|peaceiris/|codecov/|coverallsapp/" | while read line; do
        echo "    [SUSPICIOUS] Third-party action: $line" | tee -a "$REPORT_FILE"
    done
done

echo "" | tee -a "$REPORT_FILE"
echo "[Phase 4] Checking requirements.txt for suspicious entries..." | tee -a "$REPORT_FILE"
find "$SCAN_DIR" -name "requirements.txt" | while read reqfile; do
    grep -nE '(http://|https://|git\+|git://)' "$reqfile" | while read line; do
        echo "  [SUSPICIOUS] $reqfile: $line" | tee -a "$REPORT_FILE"
    done
done

echo "" | tee -a "$REPORT_FILE"
echo "[Phase 5] Git history analysis for workflow changes..." | tee -a "$REPORT_FILE"
cd "$SCAN_DIR" 2>/dev/null && git log --all --oneline --diff-filter=ACMR -- '.github/workflows/' | head -20 | while read line; do
    echo "  $line" | tee -a "$REPORT_FILE"
done

echo "" | tee -a "$REPORT_FILE"
echo "Audit Complete. Report saved to: $REPORT_FILE" | tee -a "$REPORT_FILE"

Python 脚本:SBOM 生成与漏洞匹配

import subprocess
import json
import sys
import os

def generate_sbom(target, output_format="cyclonedx-json"):
    result = subprocess.run(
        ["syft", target, "-o", output_format],
        capture_output=True, text=True, timeout=300
    )
    if result.returncode != 0:
        print(f"[ERROR] SBOM generation failed: {result.stderr[:200]}")
        return None
    return result.stdout

def scan_vulnerabilities(sbom_path):
    result = subprocess.run(
        ["grype", sbom_path, "-o", "json"],
        capture_output=True, text=True, timeout=300
    )
    if result.returncode != 0:
        print(f"[ERROR] Vulnerability scan failed: {result.stderr[:200]}")
        return None
    return json.loads(result.stdout)

def verify_signatures(image_ref):
    result = subprocess.run(
        ["cosign", "verify", "--output", "json", image_ref],
        capture_output=True, text=True, timeout=60
    )
    return {
        "verified": result.returncode == 0,
        "output": result.stdout[:500],
        "error": result.stderr[:200] if result.returncode != 0 else None
    }

def main():
    target = sys.argv[1] if len(sys.argv) > 1 else "."
    print(f"[*] Generating SBOM for: {target}")
    sbom_json = generate_sbom(target)
    if not sbom_json:
        sys.exit(1)

    sbom_file = "sbom_output.json"
    with open(sbom_file, "w") as f:
        f.write(sbom_json)
    print(f"[+] SBOM saved to {sbom_file}")

    sbom_data = json.loads(sbom_json)
    artifacts = sbom_data.get("artifacts", [])
    print(f"[+] Total packages found: {len(artifacts)}")

    print(f"[*] Scanning for vulnerabilities...")
    vuln_data = scan_vulnerabilities(sbom_file)
    if vuln_data:
        matches = vuln_data.get("matches", [])
        print(f"[+] Total vulnerabilities found: {len(matches)}")

        severity_count = {"Critical": 0, "High": 0, "Medium": 0, "Low": 0, "Negligible": 0}
        for match in matches:
            severity = match.get("vulnerability", {}).get("severity", "Unknown")
            severity_count[severity] = severity_count.get(severity, 0) + 1

        print("\n[*] Vulnerability Summary:")
        for sev, count in sorted(severity_count.items(), key=lambda x: x[1], reverse=True):
            if count > 0:
                print(f"    {sev}: {count}")

        critical_high = [m for m in matches if m.get("vulnerability", {}).get("severity") in ("Critical", "High")]
        if critical_high:
            print(f"\n[!] Critical/High vulnerabilities requiring immediate attention:")
            for match in critical_high[:10]:
                vuln = match.get("vulnerability", {})
                artifact = match.get("artifact", {})
                print(f"    [{vuln.get('severity')}] {artifact.get('name')}=={artifact.get('version')}")
                print(f"      ID: {vuln.get('id')}")
                print(f"      Fix: {vuln.get('fix', {}).get('versions', ['N/A'])}")

    if len(sys.argv) > 2:
        image = sys.argv[2]
        print(f"\n[*] Verifying signatures for: {image}")
        sig_result = verify_signatures(image)
        print(f"    Verified: {sig_result['verified']}")
        if sig_result["error"]:
            print(f"    Error: {sig_result['error']}")

if __name__ == "__main__":
    main()

YARA 规则:已知供应链恶意包特征

rule NPM_Supply_Chain_Poison_Generic {
    meta:
        description = "Detects generic npm supply chain poisoning patterns"
        author = "x7peeps"
        date = "2026-07-05"
        reference = "https://attack.mitre.org/techniques/T1195/002/"
        mitre_attack = "T1195.002"
    strings:
        $postinstall_eval = /postinstall.*eval\s*\(/
        $postinstall_exec = /postinstall.*exec\s*\(/
        $curl_obfuscated = /curl\s+.*\$\{.*\}/
        $wget_download = /wget\s+.*http/
        $env_exfil = /process\.env.*fetch\s*\(/
        $npmrc_theft = /\.npmrc/
        $ssh_key = /\.ssh\/id_/
        $base64_decode = /atob\s*\(|Buffer\.from\s*\(.*base64/
        $reverse_shell = /require\s*\(\s*['"]child_process['"]\s*\)\s*\.\s*(?:spawn|exec)/
    condition:
        2 of them
}

rule Python_Supply_Chain_Poison_Generic {
    meta:
        description = "Detects generic Python supply chain poisoning patterns"
        author = "x7peeps"
        date = "2026-07-05"
        reference = "https://attack.mitre.org/techniques/T1195/002/"
        mitre_attack = "T1195.002"
    strings:
        $setup_py_exec = "exec("
        $setup_py_system = "os.system("
        $setup_py_subprocess = "subprocess.Popen("
        $setup_py_requests = "requests.post("
        $env_exfil = /environ\s*\[/
        $base64_decode = "base64.b64decode("
        $eval_exec = "eval(__import__('"
        $reverse_shell = /socket\.connect\s*\(/
        $pip_install_hook = /pip.*install.*--force/
        $hidden_import = /__import__\s*\(/
    condition:
        2 of them
}

rule SolarWinds_SUNBURST_Strings {
    meta:
        description = "Detects SUNBURST backdoor string indicators"
        author = "x7peeps"
        date = "2026-07-05"
        reference = "https://attack.mitre.org/techniques/T1195/002/"
        mitre_attack = "T1195.002"
    strings:
        $c2_domain = "avsvmcloud.com"
        $dll_name = "SolarWinds.Orion.Core.BusinessLayer.dll"
        $mutex_prefix = "Mutex_"
        $dns_pattern = /[a-z]{8,15}\.(avsvmcloud|freescanonline|deftsecurity|thedoccloud|virtualsitting|websitetheme|highstechlabs)\.com/
    condition:
        $c2_domain or $dll_name or 2 of ($dns_pattern, $mutex_prefix)
}

0x09 公开案例分析

案例一:SolarWinds/SUNBURST (2020)

SolarWinds 供应链攻击是近年来规模最大、影响最深远的供应链攻击事件之一。攻击者(APT29/Cozy Bear,俄罗斯 SVR 情报部门)通过入侵 SolarWinds 的构建环境,将 SUNBURST 后门植入 Orion 平台的合法更新中,影响了约 18,000 个组织。

攻击链全景:

阶段时间线攻击行为MITRE ATT&CK
1. 初始入侵2019年10月入侵 SolarWinds 构建环境T1195.002 Supply Chain Compromise
2. 代码注入2019年11月向 Orion 构建流程注入 SUNBURST 后门T1059 Command and Scripting Interpreter
3. 恶意更新分发2020年2月-6月通过合法更新渠道分发感染版本T1608 Stage Capabilities
4. 域前置 C22020年3月起后门通过 CDN 中继与 C2 通信T1090.004 Domain Fronting
5. 凭证窃取2020年5月提取 SAML 签名密钥T1558 Steal or Forge Kerberos Tickets
6. 横向移动2020年5月-12月使用窃取的令牌访问受害者环境T1078 Valid Accounts
7. 数据窃取2020年6月-12月窃取邮件和文档T1005 Data from Local System

取证发现:

# 检测 SUNBURST 后门的 DNS 特征
# 后门生成的子域名格式:[base32编码的主机信息].avsvmcloud.com
python3 << 'PYTHON'
import base64

def decode_sunburst_subdomain(subdomain):
    encoded_part = subdomain.split('.')[0]
    padded = encoded_part + '=' * (8 - len(encoded_part) % 8)
    try:
        decoded = base64.b32decode(padded)
        return decoded.hex()
    except Exception:
        return "DECODE_FAILED"

suspicious_domains = [
    "dnsyncUpdate.avsvmcloud.com",
    "freescanonline.com",
    "deftsecurity.com",
]

for domain in suspicious_domains:
    parts = domain.split('.')
    if len(parts) > 2:
        sub = parts[0]
        decoded = decode_sunburst_subdomain(sub)
        print(f"[INFO] {domain} => decoded: {decoded}")
PYTHON

IOC 列表:

IOC 类型IOC 值说明
域名avsvmcloud.com主 C2 域名
域名freescanonline.comC2 域名
域名deftsecurity.comC2 域名
域名thedoccloud.comC2 域名
SHA-25632507a004932c497f5637738bc62382b...SolarWinds.Orion.Core.BusinessLayer.dll
IP20.140.0.1Microsoft 标记的恶意 IP
进程SolarWinds.BusinessLayerHost.exe宿主进程

经验教训:

  1. 构建环境是供应链安全的核心环节,必须实施严格的访问控制和审计
  2. 代码签名只能验证构建环境的完整性,不能证明构建过程中没有恶意注入
  3. 域前置等 CDN 滥用技术使 C2 检测变得极为困难
  4. 需要对软件更新进行独立的完整性验证,而非完全信任供应商

案例二:XZ Utils 后门 (2024)

XZ Utils 后门事件(CVE-2024-3094)是供应链攻击中社会工程与技术手段完美结合的典范。攻击者(疑似国家级APT,代号 “Jia Tan”)通过长达两年的社交工程,逐步获取了 XZ Utils 的维护权限,最终在 5.6.0 和 5.6.1 版本中植入了针对 OpenSSH 的后门。

攻击链全景:

阶段时间线攻击行为MITRE ATT&CK
1. 社交渗透2022年4月创建 “Jia Tan” 虚假身份,向维护者 Lasse Collin 施压T1585.001 Create Accounts
2. 权限获取2022年11月被授予 XZ Utils 仓库维护权限T1078.004 Cloud Accounts
3. 代码布局2023年-2024年初提交大量看似正常的代码修改T1195.002 Supply Chain Compromise
4. 后门植入2024年2月在构建脚本中植入恶意二进制数据T1195.002 Supply Chain Compromise
5. 版本发布2024年3月发布包含后门的 5.6.0 和 5.6.1 版本T1608 Stage Capabilities
6. 后门触发2024年3月后门在 OpenSSH 链接 liblzma 时激活T1546.006 Event Triggered Execution

技术细节:

XZ Utils 后门的精妙之处在于它的隐蔽性。恶意代码不是直接写在源码中,而是嵌入在测试用的二进制文件(tests/files/corrupted_liblzma_*.xz)中,通过构建脚本(build-to-host.m4)在编译时提取并注入到 liblzma 库中。

# 检测是否安装了受影响版本
xz --version
dpkg -l | grep xz 2>/dev/null
rpm -qa | grep xz 2>/dev/null

# 检查 liblzma 是否被注入了后门
# 后门通过 RSA 公钥验证攻击者的连接
strings /usr/lib/liblzma.so.5.6.0 | grep -iE 'id_rsa|shah|liblzma| LZMA_API'
import subprocess
import re

def check_xz_backdoor():
    result = subprocess.run(["ldd", "/usr/sbin/sshd"], capture_output=True, text=True)
    sshd_libs = result.stdout

    if "liblzma" in sshd_libs:
        print("[!] sshd links against liblzma - potential backdoor exposure")
        liblzma_path = re.search(r'liblzma\.so\S* => (\S+)', sshd_libs)
        if liblzma_path:
            path = liblzma_path.group(1)
            strings_result = subprocess.run(
                ["strings", path], capture_output=True, text=True
            )
            indicators = ["id_rsa", "sha256", "RSA_public", "lzma_implode"]
            for indicator in indicators:
                if indicator in strings_result.stdout:
                    print(f"  [CRITICAL] Found backdoor indicator: {indicator}")
            version_check = subprocess.run(["xz", "--version"], capture_output=True, text=True)
            version = version_check.stdout.strip()
            print(f"  [INFO] XZ version: {version}")
            if "5.6.0" in version or "5.6.1" in version:
                print("  [CRITICAL] Running vulnerable version!")
    else:
        print("[+] sshd does not link against liblzma")

check_xz_backdoor()

IOC 列表:

IOC 类型IOC 值说明
版本5.6.0, 5.6.1包含后门的 XZ Utils 版本
SHA-2563722223c22857e23c836c1b694f901d3...恶意 liblzma.so.5.6.0 (Debian)
域名avatars.githubusercontent.com后门通信使用的域名之一
公钥RSA 2048-bit后门中嵌入的攻击者 RSA 公钥
构建脚本build-to-host.m4恶意构建脚本

经验教训:

  1. 维护者信任是供应链安全的薄弱环节,需要多人审查和多因素认证
  2. 长期的社交工程攻击比技术漏洞更难防御
  3. 构建脚本(m4、cmake 等)的审计往往被忽视,但可以成为隐蔽的攻击载体
  4. 关键基础设施软件需要独立的安全审计和发布流程

案例三:event-stream (2018)

event-stream 是 npm 生态中供应链投毒的经典案例。攻击者通过接管一个流行的 npm 包,植入了针对 Copay(比特币钱包)的凭证窃取恶意代码。

攻击链全景:

阶段攻击行为MITRE ATT&CK
1. 包接管向维护者请求 maintainer 权限,获准后获得发布权T1195.002 Supply Chain Compromise
2. 依赖投毒添加 flatmap-stream 作为依赖,该包包含混淆的恶意代码T1195.002 Supply Chain Compromise
3. 目标定位恶意代码仅针对 Copay 比特币钱包应用T1497 Virtualization/Sandbox Evasion
4. 凭证窃取窃取 Copay 的比特币私钥和钱包数据T1552.001 Credentials In Files
5. 数据外传将窃取的数据发送到外部服务器T1041 Exfiltration Over C2 Channel

技术细节:

# 检查项目是否依赖了 event-stream
npm ls event-stream 2>/dev/null

# 检查 flatmap-stream 是否被安装
ls -la node_modules/flatmap-stream/ 2>/dev/null

# 查看 event-stream 的历史版本变更
git log --all --oneline -- node_modules/event-stream/package.json 2>/dev/null

IOC 列表:

IOC 类型IOC 值说明
npm 包名flatmap-stream恶意依赖包
npm 版本event-stream@3.3.6包含恶意依赖的版本
目标应用Copay (比特币钱包)被攻击的特定应用
SHA-2560504a04337f53705e73273f425873e88...恶意 flatmap-stream 包

经验教训:

  1. 维护者权限的授予需要严格的审查和定期复核
  2. 间接依赖(传递依赖)同样需要审计
  3. 针对特定应用的供应链攻击可以极大地缩小检测范围
  4. npm 生态中缺乏强制的包签名机制,增加了投毒的便利性

0x0A 自动化检测集成平台

综合检测流程

供应链安全检测需要将多种工具和方法整合为统一的检测流程。以下是一个端到端的自动化检测平台设计。

检测阶段工具/方法输入输出执行频率
依赖审计npm audit / pip-auditpackage.json / requirements.txt漏洞报告每次构建
SBOM 生成Syft项目目录 / 容器镜像SPDX/CycloneDX每次发布
漏洞匹配GrypeSBOMCVE 匹配报告每次发布
签名验证Cosign容器/二进制签名验证结果每次拉取
构建审计GitHub API / Git构建历史异常变更报告每日
行为监控Sigma + SIEM系统日志告警实时
固件检查CHIPSEC固件镜像安全评估季度
透明日志Rekor CLI签名记录审计轨迹每次验证

企业级实施建议

supply_chain_security_policy:
  dependency_management:
    - 使用 lockfile 锁定所有依赖版本
    - 启用 npm audit / pip-audit 自动扫描
    - 定期运行 OWASP Dependency-Check
    - 维护私有仓库镜像,禁止直接访问公共仓库
  sbom_management:
    - 每次发布生成 SBOM(使用 Syft)
    - SBOM 存储在安全的仓库中
    - 定期使用 Grype 扫描现有 SBOM
    - 建立 SBOM 签名和分发流程
  code_signing:
    - 使用 Sigstore 进行代码签名
    - 所有构建产物必须附带签名
    - 使用 Rekor 透明日志记录所有签名
    - 建立签名验证的强制性策略
  ci_cd_security:
    - 启用分支保护规则
    - 限制第三方 Action 的使用
    - 审查所有 workflow 变更
    - 使用 OIDC 而非长期 token
  monitoring:
    - 部署 Sigma 规则到 SIEM
    - 监控依赖变更和仓库修改
    - 设置新版本发布通知
    - 建立供应链安全事件响应流程

持续监控脚本

import subprocess
import json
import hashlib
import os
from datetime import datetime

class SupplyChainMonitor:
    def __init__(self, project_dir):
        self.project_dir = project_dir
        self.baseline_file = os.path.join(project_dir, ".supply-chain-baseline.json")
        self.findings = []

    def load_baseline(self):
        if os.path.exists(self.baseline_file):
            with open(self.baseline_file) as f:
                return json.load(f)
        return {"lockfile_hashes": {}, "workflow_hashes": {}, "last_scan": None}

    def save_baseline(self, baseline):
        with open(self.baseline_file, "w") as f:
            json.dump(baseline, f, indent=2)

    def calculate_file_hash(self, filepath):
        sha256_hash = hashlib.sha256()
        with open(filepath, "rb") as f:
            for byte_block in iter(lambda: f.read(4096), b""):
                sha256_hash.update(byte_block)
        return sha256_hash.hexdigest()

    def check_lockfile_integrity(self, baseline):
        lockfiles = [
            "package-lock.json", "yarn.lock", "pnpm-lock.yaml",
            "requirements.lock", "Pipfile.lock"
        ]
        for lockfile in lockfiles:
            path = os.path.join(self.project_dir, lockfile)
            if os.path.exists(path):
                current_hash = self.calculate_file_hash(path)
                saved_hash = baseline["lockfile_hashes"].get(lockfile)
                if saved_hash and saved_hash != current_hash:
                    self.findings.append({
                        "type": "LOCKFILE_MODIFIED",
                        "file": lockfile,
                        "old_hash": saved_hash[:16],
                        "new_hash": current_hash[:16],
                        "severity": "HIGH",
                        "timestamp": datetime.now().isoformat()
                    })
                baseline["lockfile_hashes"][lockfile] = current_hash

    def check_workflow_integrity(self, baseline):
        workflows_dir = os.path.join(self.project_dir, ".github", "workflows")
        if not os.path.isdir(workflows_dir):
            return
        for filename in os.listdir(workflows_dir):
            if filename.endswith((".yml", ".yaml")):
                path = os.path.join(workflows_dir, filename)
                current_hash = self.calculate_file_hash(path)
                saved_hash = baseline["workflow_hashes"].get(filename)
                if saved_hash and saved_hash != current_hash:
                    self.findings.append({
                        "type": "WORKFLOW_MODIFIED",
                        "file": filename,
                        "old_hash": saved_hash[:16],
                        "new_hash": current_hash[:16],
                        "severity": "CRITICAL",
                        "timestamp": datetime.now().isoformat()
                    })
                baseline["workflow_hashes"][filename] = current_hash

    def check_new_dependencies(self, baseline):
        pkg_json = os.path.join(self.project_dir, "package.json")
        if not os.path.exists(pkg_json):
            return
        with open(pkg_json) as f:
            pkg = json.load(f)
        all_deps = {}
        for dep_type in ["dependencies", "devDependencies", "optionalDependencies"]:
            for name, version in pkg.get(dep_type, {}).items():
                all_deps[name] = {"version": version, "type": dep_type}
        saved_deps = baseline.get("dependencies", {})
        new_deps = set(all_deps.keys()) - set(saved_deps.keys())
        removed_deps = set(saved_deps.keys()) - set(all_deps.keys())
        for dep in new_deps:
            self.findings.append({
                "type": "NEW_DEPENDENCY",
                "package": dep,
                "version": all_deps[dep]["version"],
                "dep_type": all_deps[dep]["type"],
                "severity": "MEDIUM",
                "timestamp": datetime.now().isoformat()
            })
        for dep in removed_deps:
            self.findings.append({
                "type": "REMOVED_DEPENDENCY",
                "package": dep,
                "severity": "LOW",
                "timestamp": datetime.now().isoformat()
            })
        baseline["dependencies"] = all_deps

    def run_full_scan(self):
        baseline = self.load_baseline()
        self.check_lockfile_integrity(baseline)
        self.check_workflow_integrity(baseline)
        self.check_new_dependencies(baseline)
        baseline["last_scan"] = datetime.now().isoformat()
        self.save_baseline(baseline)
        return self.findings

if __name__ == "__main__":
    import sys
    target = sys.argv[1] if len(sys.argv) > 1 else "."
    monitor = SupplyChainMonitor(target)
    findings = monitor.run_full_scan()
    print(json.dumps(findings, indent=2, ensure_ascii=False))

0x0B 参考资料

编号资料名称URL类型
1MITRE ATT&CK: Supply Chain Compromise (T1195)https://attack.mitre.org/techniques/T1195/威胁框架
2NIST SP 800-218 SSDF (Secure Software Development Framework)https://csrc.nist.gov/publications/detail/sp/800-218/final安全标准
3Sigstore 官方文档https://docs.sigstore.dev/工具文档
4npm 安全文档:防止供应链攻击https://docs.npmjs.com/security-and-compliance平台安全
5SolarWinds 事件官方报告https://www.solarwinds.com/trust-center/security-advisories事件报告
6Google Security Blog: XZ Utils 后门分析https://security.googleblog.com/安全研究
7Trail of Bits: SolarWinds 供应链攻击分析https://blog.trailofbits.com/安全研究
8OWASP Software Component Verification Standardhttps://owasp.org/scvs/安全标准
9in-toto 供应链完整性框架https://in-toto.io/工具文档
10Rekor 透明日志https://rekor.sigstore.dev/工具文档
11CHIPSEC 固件安全框架https://github.com/chipsec/chipsec工具文档
12LFX Security: 供应链安全最佳实践https://security.lfx.dev/安全指南