golang websocket编程_Apache Tomcat拒绝服务和它的WebSocket框架

616fc70e1e53f47c7b5542e49ac9f522.png

 某日,老实人的Apache Tomcat系统忽然一直提示“connection refused”,其中一个叫做WebSocket的通信框架迟迟不能交互。更要命的是,老实人一看自己电脑用户进程的CPU利用率竟然飙到99%,他恍恍惚惚地上网查找原因,原来这一切都和最近的一个漏洞有关,这种现象的专业名称叫做“拒绝服务”。为了进一步深入,老实人放下了手中正在抢购双十一折扣商品的手机,打开了google......

老实人B:“不要说那么多,先给俺看看这漏洞长啥样再进一步说话,花里胡哨的东西咱们这年纪大了消化能力不行,看着闹心”。老实人B一边说,一边拍拍自己的大肚腩,还不忘龇龇牙,一副神气的模样。

“安排!只要你开金口,你要啥都给你整一套”:老实人A

老实人B非常肯定地把手搭在A的肩膀上,放眼望向不远处熙熙攘攘的人流。A一下看穿了B内心的坚强......果然,只有男人才懂得男人想要什么。

391a9c7ea5852239912c2916faa68c26.png

01

概述&复现

老实人A:表哥们,又到了枯燥乏味的漏洞复现环节

  • 漏洞概述

2020年11月06日,正是欢乐祥和的周五,打工人沉浸在即将迎来周末的幸福之中无法自拔,也就在这一天,一位国外的表哥@RedTeam Pentesting发布了名为Tomcat WebSokcet拒绝服务漏洞的分析报告,并在自己的github上公开了漏洞EXP(exlpoit缩写,即漏洞利用代码)。该漏洞编号为CVE-2020-13935,漏洞等级为高危,漏洞评分为7.5。

利用表哥的EXP能直接对使用WebSocket 的Tomcat服务器造成影响,通过发送大量特制请求包到Tomcat服务器,可使得服务器停止响应并无法提供正常服务(啥也不说了给表哥点个赞助助兴6f110b3c0dff1002d9d4e6f9fe249639.png)。

  • 漏洞详情

Tomcat未针对WebSokcet进行包长度校验,特制的WebSocket请求包将导致处理函数无限循环,最终导致服务停机并拒绝服务。

  • 影响版本

9.0.0.M1~9.0.3610.0.0-M1~10.0.0-M68.5.0~8.5.567.0.27~7.0.104
  • 漏洞复现

老实人A用的版本为Apache Tomcat 9.0.34,正好有这个漏洞4c1374b02145d2c518f2273e19a7b301.png,如果表哥们的汤姆·猫不在影响版本范围内,想复现的话可以下个有漏洞的版本体验一下哦。

01acf79c1e32580a343a1ea9f849e2ec.png

  1. 首先搭建好Apache Tomcat的环境,进入Websocket服务界面,按下图操作打开Websocket连接。

c46b03079f26aaf0ebc6bd579efdf091.png

2.让我们看看攻击前的主机CPU状态,此时用户进程占2.6%,空闲的CPU占94.7%

789f3749b93dfdbccca8e48214847221.png

3.接下来就要使用EXP发起攻击了,由于这个EXP是用golang编写的,所以大家需要先装一下go的环境才能够执行go build命令噢(什么?还要装go???嫑慌,整个过程简单无痛,轻轻松松3minscd3ad2f59291a97b9709d18d3a8d0704.png)

# golang环境安装# First, install the packagesudo apt install -y golang# Then add the following to your .bashrcexport GOROOT=/usr/lib/goexport GOPATH=$HOME/goexport PATH=$GOPATH/bin:$GOROOT/bin:$PATH# Reload your .bashrcsource .bashrc

 “老实人太有爱了,我一定要点在看!!!”

461496ea7146d0c17e0db7a70c9130d3.png

          关门,上EXP!

9cd4dba99924443a11c2a70207d9a028.png4.大概30秒后Tomcat拒绝服务,点击WebSocket界面上的`Disconnet`按钮无回应,Tomcat系统提示:`Info: WebSocket connection closed, Code: 1006`

95d8ec05159a3474ade5f976c5bd8a8c.png

9866d2145f485e6c1594d7f90eca459a.png

5.此时再查看主机的CPU状态,用户进程几乎占满CPU,空闲的CPU为0

18bb9694bc37b0c0991509bcb060f17b.png

老实人B:“完犊子了,我的汤姆·猫好像还没升级,还是一头来自98年漏洞猫。”

“大哥淡定,经过俺一夜的分析,解析代码仅由实际需要WebSocket消息的端点触发,攻击者无法将这样的消息发送到任意的Tomcat HTTP端点。”老实人A

老实人B:“怎么说?”

“大哥后退,小弟要开始装AC了”:老实人A

“啪!”

“疼吗?”

“赶紧的,搞快点......我还有约会”

“???,向大哥敬礼......”

02

原理&分析


哎~先慢一步,来条分割线。我们老实人向来老老实实做人,一上来分析原理就设了门槛了,既然漏洞和这个WebSocket相关,那咱们先look look这个WebSocket是个什么玩意儿30e2f9e4280158e40d7452527b027369.png


  • 关于WebSocket的一点点东西

 其实在WebSocket之前,有个叫做Comet的通讯协议(这东西号称“基于HTTP长连接的服务器推技术”),是为了满足当时服务器要能实时地将更新信息传送到客户端等显示需求而生。

这种技术是在HTTP单向通信基础上模拟服务器与客户端浏览器的双向通讯,然而不同的Comet方案存在不同的缺陷,无论是跨浏览器层面还是规范限制,而且因为是一种模拟实现,所以它的效率并不高。

基于这些原因,人们一直试图从规范角度寻找一种标准的替代方案。HTML5提供了一种全新的协议来解决这个问题,这就是WebSocket。它实现了客户端与服务器之间的全双工通信,可以更好的节省服务器资源及带宽以提供实时通讯。它建立在TCP之上,与HTTP一样通过TCP传输数据,而且同样使用HTTP的默认端口;WebSocket使得通过一种标准化的方式实现客户端与服务器之间全双工通信成为可能。

WebSocket是独立的基于TCP的协议,建立WebSocket链接时,客户端首先发送一个握手请求,服务器返回一个握手响应。握手为HTTP Upgrade请求 ,因此服务器可以通过HTTP端口进行处理,并将通信切换至WebSocket协议。握手成功后,客户端与服务器之间就可以基于WebSocket协议进行全双工通信了。

WebSocket与HTTP协议完全不同,它们之间的关系仅限于WebSocket的握手是通过HTTP协议的Upgrade请求完成的。WebSocket之所以如此设计,旨在不损害网络安全的前提下解决全双工通信的问题。

        WebSocket握手请求格式如下:

b0b0cdce0426631a7cbd9c4cfae77845.png

# 请求包内容简析:1.“Upgrade:websocket”表明这是WebSocket请求,2.“Sec-WebSocket-Key”是客户端发送的一个base64编码的密文,要求服务端必须返回一个对应加密的

服务器返回的握手响应如下:

d50aa2a9f06ed90ef1e533995f686265.png

# 返回包简析:1.“HTTP 101”状态码表明服务端识别并切换为WebSocket协议2.“Sec-WebSocket-Accept”是服务端采用与客户端一致的密钥计算出来的信息
  • Tomcat如何支持WebSocket

Tomcat自7.0.5版本开始支持WebSocket,并且实现了Java WebSocket规范(JSR356 )而在7.0.5版本之前(7.0.2版本之后)则采用自定义API,即WebSocketServlet。

根据JSR356的规定,Java WebSocket应用由一系列的WebSocket Endpoint组成。Endpoint是一个Java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体WebSocket消息的接口,就像Servlet之于HTTP请求一样(不同之处在于Endpoint每个链接一个实例)。

我们可以通过两种方式定义Endpoint,第一种是编程式,即继承类javax.websocket.Endpoint并实现其方法。第二种是注解式,即定义一个POJO对象,为其添加Endpoint相关的注解。

Endpoint实例在WebSocket握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。

关于WebSocket就简单介绍到这里,表哥们感兴趣可以去网上查找相关的资料a98a5fa0e6e7511696c7f209a5357739.png

20a48a6511826c97c4f494170c68e4b0.png

接下来,主要看看(我们都爱看的)@RedTeam Pentesting表哥如何利用WebSocket框架构造恶意payload引发漏洞(看大哥时专用星星眼9ce71596d5fa01ef9bdc569221053070.png)

  • Tomcat官方的一条通告

Apache Tomcat 9.0.37的发行说明显示,已在2020年7月发现并修复了一个漏洞,内容如下:

英文版:“The payload length in a WebSocket frame was not correctly validated. Invalid payload lengths could trigger an infinite loop.  Multiple requests with invalid payload lengths could lead to a   denial of service.”中文版:“WebSocket框架中的有效负载长度未正确验证,无效的有效载荷长度可能会触发无限循环,有效载荷长度无效的多个请求可能导致拒绝服务”

翻译过后,这...这...,尔康看了都头方....,为此我们在后面需要搞明白以下几个问题:

- 什么是无效的有效载荷长度?

- 发生哪种拒绝服务?CPU或内存耗尽?甚至崩溃了?

- 在什么情况下应用程序容易受到攻击?Apache Tomcat何时解析WebSocket消息?

- 攻击者需要进行哪些准备来发起攻击?

- 对于无法进行升级的情况,是否有可能的解决方法?

  • Tomcat修复漏洞时的补丁

Apache安全团队为此漏洞链接了相应的补丁,将以下代码已添加到java/org/apache/tomcat/websocket/WsFrameBase.java中用于修复漏洞。

e2213e08fa9b96149d4d0675d9756d34.png

从这个补丁代码可以看到,代码对有效负载长度字段进行了附加检查,如果该值(payloadLength)为负,则会引发异常。但是有效载荷长度如何为负?

  • WebSocket框架结构

为了回答上面的问题,让我们看一下相应的RFC中提供的WebSocket框架结构:

3d0ec8b73e952bb4365bbb1be501df2b.png

整个数据帧的前16位包含几个位标志(FIN~MASK)以及7位有效载荷长度。如果此有效载荷长度设置为127(二进制1111111),则该图表指示应使用所谓的64位扩展有效载荷长度(如下图)

30c14460ae8ffa5ee9542d0b3eba2e80.png

WebSocket RFC指出:如果[7位有效载荷长度为127],则接下来的8个字节被解释为64位无符号整数(即最高有效位必须为0)是有效载荷长度。尽管该字段显然是64位无符号整数,但RFC另外要求最高有效位为零,这似乎是一个奇特的选择,也许是为了提供与已签名的实现互用性而做出的选择,但是这可能会引起混乱,而在这种情况下,它甚至导致了安全漏洞。

  • 构造WebSocket框架

下面按照规范进行操作,并构造一个WebSocket框架,该框架在由Apache Tomcat解析时具有负的有效载荷长度。首先,对位标志FIN, RSV1,RSV2和RSV3的值需要进行设置。FIN用于指示消息的最后一帧,由于我们要发送的整个消息都包含在单个帧中,因此将该位设置为1。RSV位保留供将来使用和WebSocket规范的扩展,因此它们都设置为零。opcode字段(4比特)表示发送的数据的类型,该值必须有效,否则将丢弃该帧。我们在后面仅发送一个简单的文本有效负载,这要求将此字段设置为值1。Go库:

github.com/gorilla/websocket中提供一个我们将要使用的常量,现在我们已经可以构造恶意WebSocket帧的第一个字节:

e821b231798f5d446ac40bc420ee6cbf.png

第二个字节的第一位是 MASK位,必须将其设置为从客户端发送到服务器的帧数。而由于有效载荷长度的大小可以变化:

  1. 如果WebSocket消息的有效负载大小不超过125个字节,则可以直接在7位有效负载长度字段中对长度进行编码。

  2. 对于126和65535之间的有效负载长度,将7位有效负载长度字段设置为常数126,并且在接下来的两个字节中将有效负载长度编码为16位无符号整数。

  3. 对于较大的有效负载,必须将7位有效负载长度字段设置为127,接下来的四个字节将有效负载长度编码为64位无符号整数。

  4. 按照这个规律,对于以64位定义的有效载荷长度,必须根据规范将最高有效位(MSB)设置为零。

d0392cfdb5390287fe2278e719b582e1.png

为了构造有效载荷长度无效的帧,从而触发Apache Tomcat实现中的不当行为,我们将后面的八个字节都设置为0xFF

98fa33309031d75075d11f678dae264a.png

然而后面发送的实际的有效负载本身远小于指定的长度,这里只发送了“test”,占4位

f9be8bcea6477771118a6af355240bd8.png

#老实人说:这里我们可以这么理解,举个例子:你和店家预定火锅,说晚上来50个人,那店家为了晚上接待你肯定拒绝接受其他顾客的服务,结果后面你就去了2人,极大地浪费了店家的资源。而在这里就是这样,你通信的时候告诉对方要来一个很大的数据包,对方根据规范给你预留了一块很大的内存,结果你就来了一丁点大的数据。那计算机不会像人这样具有主观意识,每次会话都留一块这么大的空间,而且又是个死循环,最后就导致了拒绝服务。

所以在代码中为了确保正常运行,还在发送后将连接保持打开状态30秒钟,最终数据包的组装和传输如下所示:

dbb0c2c798d7b3b09514fc1086dce8a1.png

总结一下:这个漏洞就是由于WebSokcet没有进行包长度校验,导致特制的WebSocket请求包将导致处理函数无限循环,最终导致服务停机并拒绝服务

,即CPU资源耗尽。但是,解析代码仅由实际需要WebSocket消息的端点触发。我们无法将这样的消息发送到任意的Tomcat HTTP端点,所以有一定的利用条件。

03

修复&建议&参考

  • 修复

通用修补建议:升级Tomcat版本,

临时修补建议:针对非必要服务停用 WebSocket

  • 参考

老实人参考了以下资料c75ada37e1851bee74432d8eccf0ceee.png

- [Tomcat中的WebSocket]

(https://blog.csdn.net/elinespace/article/details/52879839)

- [DIVING INTO A WEBSOCKET VULNERABILITY IN APACHE TOMCAT]

(https://blog.redteam-pentesting.de/2020/websocket-vulnerability-tomcat/)

- [RFC漏洞WebSocket框架]

(https://tools.ietf.org/html/rfc6455#section-5.2)

- [新版Tomcat下载地址]

(http://tomcat.apache.org/)

文章结束了,双十一还没结束,祝大家购物愉快!

8e9c1c268d1d86b105f589b4fe06ff4d.png

老实人,不止于老老实实做安全

616fc70e1e53f47c7b5542e49ac9f522.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值