Web协议层安全之websocket安全分析
2022-6-15 14:49:6 Author: www.freebuf.com(查看原文) 阅读量:7 收藏

WebSocket 是HTML5一种新的网络传输协议,位于 OSI 模型的应用层,可在单个TCP连接上进行全双工通信。

1.1HTTP 和 WebScoket

段落的首行要缩进,表格居中放置,如表1。 字体通一用一种雅黑或宋体,文字字号统一为5号Web 客户端和服务端之间的大多数通信使用HTTP(HTTPS也是在传输层和应用层中间加了一层SSL/TLS协议,在这里没有太大影响,故不作区分)。HTTP协议是请求-响应式的,在 HTTP1.1 开始,TCP连接可被复用。即使网络连接保持打开状态,这也将用于请求和响应的单独事务。

WebSocket和HTTP都是应用层协议,并且都基于TCP协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以建立持久性的连接,并进行双向数据传输。此外,WebSocket基于二进制帧进行传输。

RFC 6455中规定:

WebSocket通过HTTP端口80和443进行工作,并支持HTTP代理和中介,从而使其与HTTP协议兼容。

TIPS:ws协议默认80端口,wss默认443端口

为了实现兼容性,WebSocket握手使用HTTP Upgrade 头从HTTP协议更改为WebSocket协议。与HTTP协议类似, `wss` 协议建立在一个加密的TLS连接的WebSocket,而 `ws` 协议使用未加密的连接。

1.2Demo

  • 安装依赖

npm install ws

  • server.js

// 导入WebSocket模块:
const WebSocket = require('ws');

// 引用Server类:
const WebSocketServer = WebSocket.Server;

// 实例化:
const ws = new WebSocketServer({
port: 3000
});

// connection事件用于处理接入的WebSocket
ws.on('connection', function (ws) {
console.log("Connected!!!");
});

node tt.js 启动服务端

  • client

直接在浏览器的console界面输入,在服务端的终端会打印相关的日志信息

var ws = new WebSocket("ws://127.0.0.1:3000");

1.3握手过程

服务端:

<html>
<body>
<script>var ws = new WebSocket("ws://127.0.0.1:3000");</script>
Loaded!!
</body>
</html>

客户端发起握手请求

GET / HTTP/1.1
Host: 127.0.0.1:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: http://127.0.0.1
Sec-WebSocket-Key: 8PbhQOV5ykV3eYf2biw52A==
Connection: keep-alive, Upgrade
Sec-Fetch-Dest: websocket
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: same-site
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket

​如果服务端接受这个连接,它会返回一个WebSocket回复

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: dBaGq6Oh1nXvQd+sJRKFK9GnsGI=

之后,这个WebSocket的网络连接会保持开启状态,任意一方都可以直接发送WebSocket信息:

<html>
<body>
<script>
var ws = new WebSocket("ws://127.0.0.1:3000");

// 连接打开事件
ws.onopen = function() {
// 发送数据给服务端
ws.send(JSON.stringify({"message":"hello"}));
};

// 监听服务端信息
ws.onmessage = function(e){
alert(e.data);
};
</script>
Loaded!!
</body>
</html>

通过 message 监听消息

// 导入WebSocket模块:
const WebSocket = require('ws');

// 引用Server类:
const WebSocketServer = WebSocket.Server;

// 实例化:
const ws = new WebSocketServer({
port: 3000
});

// connection事件用于处理接入的WebSocket
ws.on('connection', function (ws) {
// 处理客户端的消息
ws.on('message', function (data) {
console.log(JSON.parse(data));
});

// 向客户端发送消息
ws.send("OK");

});

原则上,WebSocket消息可以包含任何内容或数据格式。在实际的应用程序中,通常WebSocket消息都是通过JSON进行发送结构化数据。

关于WebSocket握手时的一些特性:

  • Connection和Upgrade头部用来标识这是一个WebSocket握手消息。
  • Sec-WebSocket-Version请求头明确了一个客户端希望使用的WebSocket协议版本。版本13最常用。
  • Sec-WebSocket-Key请求头包含了一个base64编码的随机值,在每个WebSocket握手请求中,它一定是随机生成的。
  • Sec-WebSocket-Accept响应头的值是客户端发送的握手请求中Sec-WebSocket-key的哈希值,并与协议规范中定义的特定字符串连接。这样做的目的是匹配每一对握手请求,防止由于错误的配置或者缓存代理导致的连接错误。

1.4握手过程

基于WebSocket全双工、延迟的特性,应用场景比较广泛。

  • 聊天机器人
  • 弹幕
  • 协同编辑
  • 股票报价实施更新
  • 位置更新
  • 直播实况段落的首行要

根据上面的分析可知,WebSocket仅仅是Web程序中的一种通信协议,并不会解决Web应用中存在的安全问题。因此,原则上任何Web漏洞都有可能出现在使用WebSocket的应用中。

2.1Web漏洞

WebSocket中,用户可控的请求数据,都会涉及输入校验问题,用来规范常见的Web漏洞,如XSS、SQL Inject、RCE等。

2.1.1XSS

靶场环境:https://acb51fa71e7c52cbc04a0bac00b7009c.web-security-academy.net/

题目环境是一个商城,其中有个在线聊天功能,是基于WebSocket

to server

{"message":"<img src=1 onerror='alert(1)'>"}

to client

{"user":"You","content":"<img src=1 onerror='alert(1)'>"}

页面渲染后触发js

1655275519_62a97fff563814bba76e4.jpeg!small?1655275517146

TIPS:新版的Burp 支持WebSocket包重放

2.2 WebSocket 安全

2.2.1认证

WebSocket 协议并没有在握手阶段对客户端的身份进行认证,但服务端可以采用HTTP服务器的客户端身份认证机制,如cookie认证,HTTP 基础认证,TLS 身份认证等。因此,认证实现方面的安全问题与基于HTTP的Web认证并无区别。

  • CVE-2015-0201

Spring框架的Java SockJS客户端生成可预测的会话ID,攻击者可利用该漏洞向其他会话发送消息

  • CVE-2015-1482

Ansible Tower未对用户身份进行认证,远程攻击者通过websocket连接获取敏感信息

2.2.2授权

WebSocket 协议依然没有指定任何授权方式,因此关于权限的相关策略依然得依赖开发者在服务端实现,依然面临着垂直权限提升和水平权限提升的风险。

2.2.3跨域请求/CSWSH

WebSocket使用基于源的安全模型,在发起WebSocket握手请求时,浏览器会在请求中添加一个名为Origin的HTTP头,Oringin字段表示发起请求的源,以此来防止未经授权的跨站点访问请求。

WebSocket 的客户端不仅仅局限于浏览器,因此 WebSocket 规范没有强制规定握手阶段的 Origin 头是必需的,并且WebSocket不受浏览器同源策略、CORS机制的限制。如果服务端没有针对Origin头部进行验证可能会导致跨站点WebSocket劫持攻击。

CSWSH全称Cross-site WebSocket Hijacking,跨站点WebSocket劫持漏洞。

该漏洞最早在 2013 年被Christian Schneider 发现并公开,Christian 将之命名为跨站点 WebSocket 劫持 (Cross Site WebSocket Hijacking)(CSWSH)。跨站点 WebSocket 劫持危害大,但容易被开发人员忽视。相关案例可以参考: IPython Notebook(CVE-2014-3429), OpenStack Compute(CVE-2015-0259), Zeppelin WebSocket服务器等跨站WebSocket劫持。

攻击过程:

1655275628_62a9806ce8b45fc68d208.jpeg!small?1655275626791

需要注意的是,Origin 和 Sec-WebSocket-Key 都是由浏览器自动生成的,浏览器再次发起请求访问目标服务器会自动带上Cookie 等身份认证参数。

漏洞利用的关键点是,服务端没有对Origin头部进行校验,才能成功握手并切换到 WebSocket 协议,恶意网页就可以成功绕过身份认证连接到 WebSocket 服务器,进而窃取到服务器端发来的信息,或者发送伪造信息到服务器端篡改服务器端数据。

检测

由于跨站点WebSocket劫持攻击本质上是WebSocket握手上的CSRF漏洞,因此,首先需要确认在WebSocket握手过程中,是否进行了CSRF防护,确定是否仅仅依赖HTTP cookie进行权限鉴定。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Cookie: session=KOsEJNuflw4Rxxxxxxxxxxxxxx
Sec-WebSocket-Accept: dBaGq6Oh1nXvQd+sJRKFK9GnsGI=

TIPS:Sec-WebSocket-Key头包含一个随机值,以防止缓存代理的错误,而不是用于身份验证或会话处理的目的。

在确定握手请求存在CSRF风险的情况下,寻找使用WebSocket进行敏感数据检索的功能点。

过程:

  1. 重放确认是否存在CSRF
  2. 修改Origin头部,判断是否存在校验
对比CSRF

与传统跨站请求伪造(CSRF)攻击相比,CSRF 主要是通过恶意网页悄悄发起数据修改请求,而跨站 WebSocket 伪造攻击不仅可以修改服务器数据,还可以控制整个双向通信通道。也正是因为这个原因,Christian 将这个漏洞命名为劫持(Hijacking),而不是请求伪造(Request Forgery)。

防御
  • 检查客户端请求中的Origin信息是否跨域
  • 防止Origin头部伪造,还可以借鉴CSRF的防御机制,如增加Token验证

2.2.4 拒绝服务攻击/DOS

WebSocket设计为面向连接的协议,如果不限制连接数,可能会导致DoS风险。

F5 BIG-IP远程拒绝服务漏洞(CVE-2016-9253

  • 客户端拒绝服务

WebSocket 连接限制不同于HTTP连接限制,WebSocket有一个更高的连接限制,不同的浏览器的最大连接数也存在差异。如:火狐浏览器默认最大连接数为200。

通过发送恶意内容,占用所有Websocket,导致浏览器资源耗尽,引起拒绝服务。

  • 服务端拒绝服务
    • 发起大量连接

WebSocket建立的是持久连接,只有客户端或服务端其中一方发起关闭连接的请求,连接才会关闭。攻击者可以通过发起请求并建立大量的连接,导致服务器资源耗尽,引发拒绝服务攻击。

防御:服务端可以进行单一IP的最大连接数进行防御

  • 大数据帧占用

攻击者可以发送一个庞大的数据帧,占用服务端的内存,引发拒绝服务攻击,

防御:限制帧的大小

demo
  • js

var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({ port: 3000 });
wss.on('connection', function (ws) {
console.log('[*]Client connected!');
ws.on('message', function (message) {
console.log(message.toString('utf8'));
});
});

  • 正常客户端

<html>
<body>
<script>
var ws = new WebSocket("ws://127.0.0.1:3000");

// 连接打开事件
ws.onopen = function() {
// 发送数据给服务端
ws.send("Hello");
};

// 监听服务端信息
ws.onmessage = function(e){
alert(e.data);
};
</script>
Loaded!!
</body>
</html>

  • exp

python -m pip install ws4py from ws4py.client.threadedclient import WebSocketClient


class WS_Client(WebSocketClient):

# 需要重写以下三个方法
def opened(self):
reqData = "Hello"
self.send(reqData)

def closed(self, code, reason=None):
print("[-] Closed down:", code, reason)

def received_message(self, resp):
resp = json.loads(str(resp))
print(resp)


if __name__ == '__main__':
while True:
ws = WS_Client("ws://127.0.0.1:3000")
ws.connect()

2.2.5 中间人攻击

WebSocket使用HTTP或HTTPS协议进行握手请求,在使用HTTP协议的情况下,若存在中间人可以嗅探HTTP流量,那么中间人可以获取并篡改WebSocket握手请求,通过伪造客户端信息与服务器建立WebSocket连接。短文章可以不引用目录

WebSocket 是HTML5中一个及时的全双工通讯协议,在性能上有着明显的优势,但他并不能解决安全问题,同时也需要开发者考虑其安全威胁面。

https://christian-schneider.net/CrossSiteWebSocketHijacking.html

https://www.liaoxuefeng.com/wiki/1022910821149312/1103327377678688

https://segmentfault.com/q/1010000020661067

https://www.mi1k7ea.com/2019/10/04/CSWSH%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/#0x02-CSWSH%E6%BC%8F%E6%B4%9E

https://security.tencent.com/index.php/blog/msg/119

https://www.mi1k7ea.com/2021/01/30/%E6%B5%85%E6%9E%90WebSocket%E5%AE%89%E5%85%A8/

https://www.freebuf.com/articles/web/252298.html

https://wiki.wgpsec.org/knowledge/web/websocket-sec.html

https://www.mi1k7ea.com/2021/01/30/%E6%B5%85%E6%9E%90WebSocket%E5%AE%89%E5%85%A8/


文章来源: https://www.freebuf.com/articles/web/336291.html
如有侵权请联系:admin#unsafe.sh