Linux应急响应 - 14.7 供应链攻击检测

供应链攻击检测与应急响应

供应链攻击通过篡改软件的构建、分发、更新环节来植入恶意代码,是最隐蔽的攻击方式之一

关联:06-文件系统取证 | 30-YARA规则

供应链攻击概述

攻击类型分类

类型 描述 经典案例
软件包投毒 在官方/第三方包仓库中植入恶意包 event-stream (npm), ua-parser-js
依赖混淆 利用同名内部包在公共仓库注册 Dependency Confusion (Alex Birsan)
源码篡改 直接篡改开源项目代码 SolarWinds, codecov
构建环境篡改 篡改 CI/CD 管道 CircleCI 事件
镜像投毒 篡改 Docker 镜像或系统镜像源 Docker Hub 恶意镜像
二进制篡改 替换分发的编译产物 CCleaner, ASUS LiveUpdate

Linux 环境中的主要风险

系统包管理器(apt/yum/dnf)的源被替换或包被篡改

Python pip / Node.js npm / Ruby gem 等语言包管理器

Docker 镜像(基础镜像被篡改)

自编译软件的源码被篡改

CI/CD 流水线中的依赖被注入

系统包完整性校验

Debian/Ubuntu (dpkg/apt)

debsums 校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 安装 debsums
apt-get install debsums

# 校验所有已安装包的文件完整性
debsums -c 2>/dev/null
# -c 只显示被修改的文件

# 校验特定包
debsums openssh-server
debsums coreutils

# 校验关键系统工具
for pkg in coreutils procps net-tools iproute2 openssh-server; do
echo "=== $pkg ==="
debsums $pkg 2>/dev/null
done

dpkg 验证

1
2
3
4
5
6
7
8
9
10
11
12
# 查看包文件列表
dpkg -L openssh-server

# 验证包签名
apt-key list

# 检查 apt 源
cat /etc/apt/sources.list
ls -la /etc/apt/sources.list.d/

# 查找非官方源
grep -r "^deb " /etc/apt/sources.list /etc/apt/sources.list.d/ | grep -v "ubuntu.com\|debian.org"

RHEL/CentOS (rpm/yum)

rpm -Va 校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 验证所有已安装包的文件
rpm -Va

# 输出格式解读:
# S 文件大小变化
# M 权限/类型变化
# 5 MD5 校验和变化
# D 设备号变化
# L 符号链接变化
# U 用户所有者变化
# G 组所有者变化
# T 修改时间变化
# P 功能(capabilities)变化

# 重点关注:S.5....T 表示内容和时间都变了(高危)
rpm -Va | grep "^S\.5"

# 校验特定包
rpm -V openssh-server
rpm -V coreutils
rpm -V procps-ng

yum 源检查

1
2
3
4
5
6
7
8
9
10
11
# 检查 yum 源配置
cat /etc/yum.repos.d/*.repo

# 查看启用的源
yum repolist

# 检查 GPG key
rpm -qa gpg-pubkey*

# 验证 RPM 包签名
rpm -K /path/to/package.rpm

关键二进制文件校验

排查思路

如果攻击者替换了系统命令(如 ls, ps, netstat),那么用这些命令排查就会得到假结果

这就是为什么要先校验工具本身的完整性

手动校验方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 对比关键二进制的 SHA256 与包管理器记录

# Ubuntu: 获取包中文件的原始 hash
dpkg -V coreutils

# CentOS: rpm 验证
rpm -V coreutils

# 手动计算 hash 并与已知 good hash 对比
sha256sum /usr/bin/ls
sha256sum /usr/bin/ps
sha256sum /usr/bin/ss
sha256sum /usr/bin/netstat
sha256sum /usr/sbin/sshd
sha256sum /usr/bin/find
sha256sum /usr/bin/lsof

# 检查文件属性(大小、权限、时间戳)
stat /usr/bin/ps
stat /usr/bin/ls

# 对比 file 类型(被替换的可能是脚本而非 ELF)
file /usr/bin/ps # 应该是 ELF 64-bit
file /usr/bin/ls # 应该是 ELF 64-bit

使用静态编译工具

当怀疑系统工具被替换时,使用静态编译的可信工具

1
2
3
4
5
6
7
8
9
# busybox 是一个静态编译的多功能工具,不依赖系统库
# 可以从可信源下载
busybox ls /tmp/
busybox ps aux
busybox netstat -antlp

# 或使用自带的 python 替代
python3 -c "import os; print(os.listdir('/tmp/'))"
python3 -c "import subprocess; print(subprocess.check_output(['cat', '/etc/passwd']).decode())"

语言包管理器检测

Python pip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 列出所有安装的 pip 包
pip3 list --format=columns

# 检查包来源
pip3 show <package_name>

# 检查包完整性(对比 hash)
pip3 hash <package_file>.whl

# 查找可疑的包名(typosquatting)
# 例如 "requets" vs "requests"
pip3 list | sort

# 检查安装脚本(setup.py 可能执行恶意代码)
pip3 show -f <package_name>

# 检查虚拟环境
find / -name "site-packages" -type d 2>/dev/null

Node.js npm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 列出全局包
npm list -g --depth=0

# 检查项目依赖
npm ls

# 审计已知漏洞
npm audit

# 检查 postinstall 脚本(可能执行恶意代码)
cat node_modules/<pkg>/package.json | grep -A5 '"scripts"'

# 查找 .npmrc 配置(可能指向恶意源)
find / -name ".npmrc" 2>/dev/null -exec cat {} \;

Ruby gem / Go modules

1
2
3
4
5
6
# Ruby
gem list
gem environment

# Go
go list -m all

Docker 镜像安全检测

镜像检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 列出所有镜像
docker images

# 检查镜像历史(每一层做了什么)
docker history <image_name>

# 检查镜像的 Dockerfile 指令
docker inspect <image_name> | jq '.[0].Config.Cmd'
docker inspect <image_name> | jq '.[0].Config.Entrypoint'
docker inspect <image_name> | jq '.[0].Config.Env'

# 导出镜像文件系统进行离线分析
docker export <container_id> > container_fs.tar

# 检查基础镜像是否为官方版本
docker inspect <image_name> | jq '.[0].RepoDigests'

容器运行时检查

1
2
3
4
5
6
7
8
9
10
11
# 检查运行中的容器
docker ps -a

# 检查容器挂载和权限
docker inspect <container_id> | jq '.[0].Mounts'
docker inspect <container_id> | jq '.[0].HostConfig.Privileged'
docker inspect <container_id> | jq '.[0].HostConfig.CapAdd'

# 容器逃逸指标
docker inspect <container_id> | jq '.[0].HostConfig.PidMode' # "host" 表示共享 PID
docker inspect <container_id> | jq '.[0].HostConfig.NetworkMode' # "host" 表示共享网络

CI/CD 管道检查

检查构建环境

审查 CI/CD 配置文件:.github/workflows/, .gitlab-ci.yml, Jenkinsfile

检查是否引用了不受信任的 Action/Plugin

检查环境变量中的 secrets 是否泄露

检查构建产物的 hash 是否与预期一致

常见风险

使用 @main@latest 引用(而非固定版本/hash)

在构建过程中执行外部脚本(curl | bash 模式)

构建 Agent 被攻陷

Secrets 在日志中泄露

实战案例

案例:发现被篡改的 sshd

场景:安全审计发现 sshd 文件的 hash 与官方不匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 验证 sshd 包完整性
# Ubuntu
debsums openssh-server
# 输出: /usr/sbin/sshd FAILED

# CentOS
rpm -V openssh-server
# 输出: S.5....T. /usr/sbin/sshd

# 2. 分析被篡改的 sshd
file /usr/sbin/sshd # 确认文件类型
stat /usr/sbin/sshd # 查看修改时间
strings /usr/sbin/sshd | grep -iE "password|log|send|connect" # 查找后门特征

# 3. 与原始版本对比
apt-get download openssh-server # 下载原始包
dpkg -x openssh-server_*.deb /tmp/orig-ssh/
diff <(md5sum /usr/sbin/sshd) <(md5sum /tmp/orig-ssh/usr/sbin/sshd)

# 4. 恢复原始文件
apt-get install --reinstall openssh-server

排查清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 一键供应链检查脚本
echo "=== 系统包完整性 ==="
if command -v debsums &>/dev/null; then
debsums -c 2>/dev/null | head 20
elif command -v rpm &>/dev/null; then
rpm -Va 2>/dev/null | grep "^S\.5" | head 20
fi

echo "=== 关键二进制校验 ==="
for bin in /usr/bin/ps /usr/bin/ls /usr/bin/ss /usr/sbin/sshd /usr/bin/find; do
echo -n "$bin: "
file "$bin" | grep -q "ELF" && echo "OK (ELF)" || echo "SUSPICIOUS (not ELF!)"
done

echo "=== APT/YUM 源检查 ==="
grep -r "^deb " /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null
cat /etc/yum.repos.d/*.repo 2>/dev/null | grep "^baseurl"

echo "=== /etc/ld.so.preload ==="
cat /etc/ld.so.preload 2>/dev/null

echo "=== Docker 镜像 ==="
docker images --format "{{.Repository}}:{{.Tag}} {{.Size}}" 2>/dev/null

实战练习

练习场景

系统中的某个关键二进制文件被替换

使用本页学到的方法发现并恢复

排查步骤

  1. 使用 debsums/rpm -Va 发现被修改的文件

  2. 使用 file/strings 分析被修改的文件

  3. 对比原始版本

  4. 恢复并加固


上一章 目录 下一章
14.6-勒索病毒应急 Linux应急响应 15-Crontab后门