本文目录导读:

- 第一阶段:预防性检测(在代码进入管道前阻断)
- 第二阶段:运行时监控(在CI/CD执行过程中发现)
- 第三阶段:事后取证与溯源(发现已潜入的恶意代码)
- 高级发现技巧:异常模式识别
- 关键工具推荐
- 总结:发现恶意注入的“黄金法则”
恶意代码注入CI/CD管道是供应链攻击的一种高级形式,攻击者通常通过篡改源代码、依赖库、构建脚本或部署配置来植入后门。发现这种攻击需要从“人、工具、流程”三个维度建立多层检测机制。
以下是具体且可操作的发现方法,按检测时机和深度分类:
第一阶段:预防性检测(在代码进入管道前阻断)
这是成本最低的发现方式,重点在于识别代码与历史行为模式的偏差。
-
代码变更审计(Code Diff Review)
- 人工+自动化结合:任何PR(Pull Request)都应触发自动化差异对比,重点关注:
- 非开发人员的提交:检查提交者邮箱、SSH Key是否属于已知成员。
- 异常时间/频率:凌晨3点的批量提交、短时间内对多个核心文件的修改。
- 隐藏修改:使用
git diff --ignore-space-change检查空格替换(如用全角空格替换半角,不改变显示但改变哈希值)。 - 二进制文件:禁止直接提交
.exe,.dll,.jar,.pyc等文件,除非有明确审批。
- 人工+自动化结合:任何PR(Pull Request)都应触发自动化差异对比,重点关注:
-
依赖项扫描(SCA - 软件组成分析)
- 已知漏洞库:使用
Trivy,Snyk,Dependabot等工具扫描package.json,requirements.txt,pom.xml。 - 行为分析(重点) :并非所有恶意包都有CVE编号,需关注:
- 新引入的包:在CI中自动对比
go.sum或yarn.lock的差异,标记来源不明或下载量极低的包。 - Typosquatting(拼写欺骗) :如
requstsvsrequests,urllib3vsurllb3。 - Telemetry(遥测) :扫描包是否包含对
curl,wget,netcat,base64解码到执行等行为的调用。
- 新引入的包:在CI中自动对比
- 已知漏洞库:使用
-
预提交Hook(Pre-commit Hooks)
- 在代码推送之前,在开发者本地运行钩子脚本,禁止:
- 硬编码的密钥或令牌(使用
git-secrets或truffleHog)。 - 包含
eval,exec,base64 -d | sh等危险模式的代码片段。
- 硬编码的密钥或令牌(使用
- 在代码推送之前,在开发者本地运行钩子脚本,禁止:
第二阶段:运行时监控(在CI/CD执行过程中发现)
当恶意代码已经通过第一层防护进入管道后,需要在构建、测试、部署过程中进行动态监测。
-
不可变构建环境
- 原则:每次构建使用干净的Docker容器,禁止使用缓存或宿主机上的遗留文件。
- 检测点:
- 文件系统异常:监控
/tmp,/var/tmp,/dev/shm中是否在构建过程中出现非预期的文件。 - 网络外连:构建过程中,容器是否尝试连接外部IP(如
curl example.com/malware)。这是最关键的信号,使用iptables限制构建容器出站,或使用工具监控NetworkPolicy。
- 文件系统异常:监控
-
构建日志行为分析
- 命令执行异常:在
Jenkinsfile,.gitlab-ci.yml,cloudbuild.yaml中,检查:- 混淆脚本:
python3 -c "import base64; exec(...)"或eval $(echo ...|base64 -d)。 - 环境变量劫持:是否存在
export PATH=/tmp:$PATH或修改LD_PRELOAD/GEM_PATH的语句。 - 秘密泄露:日志中是否意外出现
AWS_SECRET_KEY或DB_PASSWORD(攻击者可能利用构建阶段的信息窃取)。
- 混淆脚本:
- 工具调用模式:正规CI很少在构建环境执行
ps aux,cat /etc/passwd,id等命令。
- 命令执行异常:在
-
依赖下载源验证
- MITM攻击:检查CI/CD仓库(如Artifactory, Nexus)中的包哈希值是否与官方仓库(npmjs.org, pypi.org)一致。
- 镜像污染:如果使用内部镜像源,需实时审计镜像源的访问日志,发现尝试下载
/a/valid-package但实际解析到/b/malicious的情况。
第三阶段:事后取证与溯源(发现已潜入的恶意代码)
对于已经在运行一段时间、看似正常的管道,需要主动进行“体检”。
-
流水线脚本完整性校验
- Git历史审计:检查
.gitlab-ci.yml或Jenkinsfile的历史版本。- 寻找强制推送(
--force)的记录,这可能是覆盖恶意提交痕迹。 - 回滚冲突:有人刻意回滚了一个安全修复,这可能意味着恶意代码需要旧版漏洞。
- 寻找强制推送(
- Git历史审计:检查
-
制品与镜像深度扫描(运行时安全)
- SBOM(软件物料清单)对比:将线上部署的容器镜像的SBOM与CI构建时的SBOM进行对比,如果发现线上镜像包含构建时没有的组件,很可能被注入了。
- 动态行为分析:在沙箱中运行最终制品(Docker镜像),监控其行为:
- 是否向私有C2(命令与控制)服务器发送心跳?
- 是否尝试修改
/etc/hosts或 DNS 设置?
- 签名验证:所有制品和镜像必须经过
cosign或notary签名,在部署前,验证签名是否源自合法的CI Job Runner。
-
身份与访问审计
- 令牌泄露:CI/CD中使用的
GITHUB_TOKEN,SLACK_WEBHOOK_URL等是否意外出现在公开仓库(GitHub Secrets Scanning)或日志中。 - 用户行为异常:一个只负责前端的开发者,是否突然修改了数据库连接池配置?一个长期不活跃的CI账户,是否突然触发了生产环境部署?
- 令牌泄露:CI/CD中使用的
高级发现技巧:异常模式识别
- 时间窗口攻击:攻击者在某个时间点修改代码,使其在特定时间(如周末、午夜)或特定条件(如环境变量
PRODUCTION=true)下才执行恶意逻辑。 - 多源注入:在开源依赖包中埋下逻辑炸弹(如删除数据库的代码),等待上游CI构建时自动引入。
- Pipeline as Code 后门:在
Jenkinsfile中通过library引用一个看似无害的共享库,但该库的v1.0.1版本已被埋入后门。
关键工具推荐
| 阶段 | 工具 | 功能 |
|---|---|---|
| 代码与依赖 | Semgrep / CodeQL |
自定义规则检测恶意代码模式(如 eval(base64.decodestring))。 |
OSV-Scanner |
Google开源的依赖漏洞扫描,覆盖面广。 | |
| 构建环境 | Falco (作为sidecar) |
Kubernetes原生运行时安全,可监控CI Job的容器行为。 |
Tetragon |
基于eBPF的实时监控,能检测到进程创建、文件读取、网络连接等底层系统调用。 | |
| 全流程治理 | SLSA Framework |
框架标准,要求生成不可伪造的构建证明,防止构建过程被篡改。 |
Sigstore / Cosign |
确保代码签名和镜像签名是出自合法的流水线。 |
发现恶意注入的“黄金法则”
- 不可变性:任何构建依赖(包括镜像、基础镜像、工具链)的版本和内容都必须锁定,变更是最可疑的信号。
- 网络隔离:CI运行环境除明确允许的(如包管理器源、容器仓库)外,禁止一切出站流量,恶意代码通常需要外连来接收指令或外传数据。
- 最小权限:CI Job的Token只应具有完成其任务的最小权限(如只读代码库,不能修改流水线配置),如果攻击者获取了Token,他能做的事就很少。
- 主动监控:不信任任何提交,每次CI运行都视为一次潜在的威胁,监控其行为并与“基线”进行对比。
最容易被忽视的漏洞往往是“修改流水线配置的权力”本身。 如果有人能修改 .gitlab-ci.yml 或 Jenkinsfile 并提交,他几乎可以执行任何命令。对流水线配置文件的变更实施双人审核(4-eyes principle) 是最直接且有效的发现手段。