漏洞分析 - Apache Tomcat WebSocket DoS (CVE-2020-13935)
字数 1976 2025-08-19 12:41:18
Apache Tomcat WebSocket DoS漏洞分析 (CVE-2020-13935) 教学文档
漏洞概述
漏洞名称: WebSocket DoS Vulnerability in Apache Tomcat (CVE-2020-13935)
漏洞描述: Apache Tomcat的WebSocket实现中存在一个拒绝服务(DoS)漏洞,攻击者可以通过发送特制的WebSocket帧导致服务器进入无限循环,消耗CPU资源,最终导致服务不可用。
影响版本:
- 10.0.0-M1 to 10.0.0-M6
- 9.0.0.M1 to 9.0.36
- 8.5.0 to 8.5.56
- 8.0.1 to 8.0.53
- 7.0.27 to 7.0.104
漏洞原理分析
WebSocket帧结构
根据RFC 6455,WebSocket帧的基本结构如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
载荷长度处理机制
WebSocket帧中的"负载长度"(payload length)处理分为三种情况:
-
短长度(≤125字节):
- 直接使用7-bit payload length字段表示
-
中等长度(126-65535字节):
- 7-bit payload length字段设置为126
- 接下来的2个字节(16-bit)作为扩展长度
-
长长度(>65535字节):
- 7-bit payload length字段设置为127
- 接下来的8个字节(64-bit)作为扩展长度
RFC 6455特别要求: 对于64-bit扩展长度,最高有效位(MSB)必须为0。
漏洞根源
漏洞存在于java/org/apache/tomcat/websocket/WsFrameBase.java文件中,Tomcat在处理WebSocket帧时:
- 当payload length设置为127时,会读取接下来的8个字节作为64-bit扩展长度
- 未检查这64-bit扩展长度的最高有效位是否为0
- 如果最高有效位为1,Java会将其解释为负数(因为Java没有无符号整数类型)
- 后续处理负长度时导致无限循环
漏洞验证与利用
构造恶意WebSocket帧
以下是构造恶意帧的关键步骤:
-
设置帧头:
- FIN=1 (表示这是消息的最后一帧)
- RSV1-3=0 (保留位设为0)
- opcode=1 (文本帧)
-
设置长度字段:
- MASK=1 (客户端到服务器的帧必须掩码)
- 7-bit payload length=127 (表示使用64-bit扩展长度)
- 扩展长度字段设置为8个0xFF字节(违反RFC规范)
-
设置掩码键和载荷:
- 使用简单的掩码键(如全0)
- 实际载荷可以很短(如"test")
PoC代码关键部分
var buf bytes.Buffer
// 设置帧头
fin := 1
rsv1 := 0
rsv2 := 0
rsv3 := 0
opcode := websocket.TextMessage
buf.WriteByte(byte(fin<<7 | rsv1<<6 | rsv2<<5 | rsv3<<4 | opcode))
// 设置长度字段
buf.WriteByte(byte(1<<7 | 0b1111111)) // MASK=1, payload len=127
// 设置8个0xFF作为扩展长度(违反RFC规范)
buf.Write([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})
// 设置掩码键(简单全0)
maskingKey := []byte{0, 0, 0, 0}
buf.Write(maskingKey)
// 设置实际载荷
buf.WriteString("test")
检测方法
- 搭建受影响版本的Tomcat环境(如9.0.31)
- 寻找WebSocket端点(如Tomcat自带的examples:
http://localhost:8080/examples/websocket/echo.xhtml) - 使用PoC连接到WebSocket端点(如
ws://localhost:8080/examples/websocket/echoProgrammatic) - 观察服务器CPU使用率是否持续100%
修复方案
官方修复
修复提交在commit 40fa74c7,主要修改了WsFrameBase.java文件,增加了对payload length的校验:
// The most significant bit of those 8 bytes is required to be zero
// (see RFC 6455, section 5.2). If the most significant bit is set,
// the resulting payload length will be negative so test for that.
if (payloadLength < 0) {
throw new WsIOException(
new CloseReason(CloseCodes.PROTOCOL_ERROR,
sm.getString("wsFrame.payloadMsbInvalid")));
}
修复建议: 升级到以下或更高版本:
- 10.0.0-M7+
- 9.0.37+
- 8.5.57+
- 8.0.54+
- 7.0.105+
临时缓解措施
如果无法立即升级:
- 禁用WebSocket功能: 如果不使用WebSocket,可以完全禁用
- 限制访问: 通过WAF或防火墙限制对WebSocket端点的访问
- 屏蔽默认端点: 拦截Tomcat默认WebSocket功能的URL,如:
/examples/websocket/echo.xhtml/examples/websocket/echoProgrammatic/examples/websocket/echoAnnotation- 其他examples相关端点
技术总结
- 漏洞类型: 拒绝服务(DoS)
- 触发条件: 发送特制WebSocket帧,其中64-bit扩展长度的最高位为1
- 影响: 导致Tomcat进程CPU使用率100%,服务不可用
- 利用难度: 低,无需认证,构造简单
- 修复关键: 严格遵循RFC 6455规范,校验扩展长度的最高位