一张一弛之Websocket攻防总结
概念
01
通过http协议向服务端发送协议升级请求
GET /ws HTTP/1.1
Sec-WebSocket-Key: VfKuQG4BzhBYkNvraRlHUA==
Connection: keep-alive, Upgrade
Upgrade: websocket
02
服务端返回状态码101,同意升级
HTTP/1.1 101
Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: mjBz+76GMZxXOSkefWtoVHlPhU0=
03
传输数据
ws.onopen=function () {
ws.send("你好");
}
实现
2.1 客户端
01
创建对象
var Socket = new WebSocket(url, [protocol] );
02
该对象的4个事件
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Socket.onopen=function() {
console.log("websocket状态:"+Socket.readyState);
};
Socket.onmessage=function(aaa){
console.log("收到消息:"+ aaa.data);
};
Socket.onclose=function() {
console.log("连接已关闭");
};
03
该对象的1个属性
|
|
|
|
var readyState =Socket.readyState;
04
该对象的2个方法
|
|
|
|
|
|
Socket.onopen= function () {
Socket.send('hello'); //发送数据
Socket.close();
};
2.2 服务端
01
设置socket监听,等待连接
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(("127.0.0.1",8080))
sock.listen(5)
conn,addr = sock.accept()
data = conn.recv(8096)
02
准备返回包
response_tpl ="HTTP/1.1101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
magic_string ='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = headers['Sec-WebSocket-Key'] +magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response_str = response_tpl % (ac.decode('utf-8'))
conn.send(bytes(response_str, encoding='utf-8'))
03
处理收到的数据
表示是否要对数据载荷进行掩码操作
从客户端向服务端发送数据时,需要对数据进行掩码操作
从服务端向客户端发送数据时,不需要对数据进行掩码操作
0~126:扩展头部长度为0字节,后面全部为主体数据
126:扩展头部长度为2字节,后面全部为主体数据
127:扩展头部长度为8字节,后面全部为主体数据
Masking-key:mask为1时4个字节,用于解密后面的数据
Payload Data:密文数据
info = conn.recv(8096)
payload_len = info[1] &127
# 主体数据中的前四字节为Masking-key,用于解码后面的消息
if payload_len ==126:
extend_payload_len =info[2:4]
mask = info[4:8]
decoded =info[8:]
elifpayload_len ==127:
extend_payload_len =info[2:10]
mask = info[10:14]
decoded =info[14:]
else:
extend_payload_len =None
mask = info[2:6]
decoded =info[6:]
bytes_list =bytearray()
for i inrange(len(decoded)):
chunk = decoded[i] ^ mask[i %4]
bytes_list.append(chunk)
body =str(bytes_list,encoding='utf-8')
04
处理即将发送的数据
msg_bytes =bytes('hello',encoding="utf-8")
token = b"\x81"#1000 0001
length =len(msg_bytes) #5
if length <126:
token += struct.pack("B", length)
elif length <=0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes # b'\x81\x05hello'
conn.send(msg)
2.3 实例
01
客户端
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
</head>
<body>
<div>发送:</div>
<input type="text" id="msgContent"/>
<input type="button" value="发送" onclick="CHAT.chat()"/>
<!--
<div>接收:</div>
<divid="receiveMsg" style="background-color:gainsboro;"></div>
!-->
<script type="application/javascript">
var msg =document.getElementById("msgContent");
window.CHAT= {
socket:null,
init:function() {
if (window.WebSocket) {
CHAT.socket=newWebSocket("ws://127.0.0.1:8080");
CHAT.socket.onopen=function() {
console.log("success");
},
CHAT.socket.onclose=function() {
console.log("close");
},
CHAT.socket.onerror=function() {
console.log("err");
},
CHAT.socket.onmessage=function(e) {
console.log("发送:\n"+ msg.value)
console.log("接收:\n"+ e.data);
// var receiveMsg =document.getElementById("receiveMsg");
// var html = receiveMsg.innerHTML;
// receiveMsg.innerHTML = html + "<br/>" +e.data + "<br/>";
}
} else {
alert("buzhichi");
}
},
chat:function() {
CHAT.socket.send(msg.value);
}
};
CHAT.init();
</script>
</body>
</html>
02
服务端
import os
import socket
import base64
import struct
import hashlib
def get_headers(data):
# 将http请求头数据转换成字典
header_dict = {}
for i in data.decode('utf-8').split('\r\n'):
if ': ' in i:
k,v = i.split(': ', 1)
header_dict[k] = v
return header_dict
def get_data(info):
# 处理收到的数据
payload_len = info[1] & 127
if payload_len == 126:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif payload_len == 127:
extend_payload_len = info[2:10]
mask = info[10:14]
decoded = info[14:]
else:
extend_payload_len = None
mask = info[2:6]
decoded = info[6:]
bytes_list = bytearray()
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
body = str(bytes_list, encoding='utf-8')
return body
def send_msg(conn, msg_bytes):
# 处理即将发送的数据
token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length <= 0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
conn.send(msg)
return True
def start():
# 创建socket对象,监听请求
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(("127.0.0.1",8080))
sock.listen(5)
conn,addr = sock.accept()
data = conn.recv(8096)
# 拼接返回包
headers = get_headers(data)
response_tpl = "HTTP/1.1 101 Switching Protocols\r\nUpgrade:websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n\r\n"
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = headers['Sec-WebSocket-Key'] + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
# 发送返回包
response_str = response_tpl % (ac.decode('utf-8'))
conn.send(bytes(response_str, encoding='utf-8'))
# 收发数据
while True:
data = conn.recv(8096)
data = get_data(data)
print(data)
# send_msg(conn,bytes(data,encoding="utf-8"))
cmd = os.popen(data)
send_msg(conn,bytes(cmd.read(),encoding="utf-8"))
if __name__ == "__main__":
start()
03
演示
漏洞
3.1 XSS
01
漏洞
02
修复
def html_trans(data):
data = data.replace('<','<')
data = data.replace('>','>')
return data
3.2 DOS
01
漏洞
可以向服务端发起大量申请建立websocket连接的请求,建立持久连接,消耗服务器资源
可以发送一个单一的庞大的数据帧(如:2^16),或者发送一个长流的分片消息的小帧,消耗服务器资源
02
修复
设置单IP可建立连接的最大连接数
限制帧大小和多个帧重组后的总消息大小
01
漏洞
02
修复
if headers['Cookies'] == xxx:
...
conn.send(bytes(response_str, encoding='utf-8'))
3.4 CSWSH
01
漏洞
01
修复
if headers['Token'] == xxx:
...
conn.send(bytes(response_str, encoding='utf-8'))
参考
https://www.cnblogs.com/ssyfj/p/9245150.html
https://www.cnblogs.com/lichmama/p/3931212.html
https://security.tencent.com/index.php/blog/msg/119
https://www.cnblogs.com/songwenjie/p/8575579.html
http://www.ruanyifeng.com/blog/2017/05/websocket.html
https://developer.ibm.com/zh/articles/j-lo-websocket-cross-site/