Web Sockets
Web Sockets¶
- A heartbeat is built in to the protocol
- Uses the wss:// or ws:// schema
- Parsing the schema is weird Schema RFC
- Do not tunnel other services though this because it would allow services to preform XSS attacks
- Websockets do not respect CORS
Handshake¶
Initial Upgrade Request:
GET /?encoding=text HTTP/1.1
Host: echo.websocket.org
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Sec-WebSocket-Version: 13
Origin: https://www.websocket.org
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: 9lzQkbbAscpmOiwYWVSgMg==
DNT: 1
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
Initial Upgrade Response:
HTTP/1.1 101 Web Socket Protocol Handshake
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Headers: x-websocket-extensions
Access-Control-Allow-Headers: x-websocket-version
Access-Control-Allow-Headers: x-websocket-protocol
Access-Control-Allow-Origin: https://www.websocket.org
Connection: Upgrade
Date: Mon, 15 Mar 2021 21:32:46 GMT
Sec-WebSocket-Accept: H82Hn1AbYRO3N7uzo6yysC0KdM0=
Server: Kaazing Gateway
Upgrade: websocket
WebSocket-Key¶
The Sec-WebSocket-Accept
value is generated from the initial key sent from the client and a static value in the RFC.
Generating the Sec-WebSocket-Accept:
import hashlib, base64
Static_Value = b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
WebSocket_Key = b'9lzQkbbAscpmOiwYWVSgMg=='
out = hashlib.sha1(WebSocket_Key + Static_Value).digest()
print(base64.b64encode(out))
#b'H82Hn1AbYRO3N7uzo6yysC0KdM0='
URL Parsing¶
- Web Socket URLs parse different from HTTP URLs
import urlparse
print(urlparse.urlparse('wss://foo/?bar=baz'))
#ParseResult(scheme='wss', netloc='foo', path='/?bar=baz', params='', query='', fragment='')
Authentication/Authorization¶
- There is no authentication in the Protocol. The application must use TLS, HTTP Headers or Cookies to authenticate.
- Need to Connect the Client IP to the account that requested the Websocket Upgrade.
Origin Header¶
- Not restrained by Cross Origin Policy
- Origin is sent in the Upgrade request
- Make sure that the Server limits the allowed origins by checking the Origin Header in the Upgrade request
- If this is not set then an attacker can so Cross Origin WebSockets. This includes sending request and getting responses responses
Framing¶
Tunneling¶
Security¶
Authentication/Authorization¶
Access Control:
Cross-Site WebSocket Hijacking (CSWSH)¶
Note
Make sure that you are checking the Origin Header
Websockets are not restricted by Cross Origin Policy. You can make a Websocket from another domain and this will send cookies alongside with it. This is up to the server to restrict.
Example CSWSH Request:
GET /v3/channel_1?api_key=VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV¬ify_self HTTP/1.1
Host: demo.piesocket.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: http://livepersoninc.github.io
Sec-WebSocket-Key: B65ortnvry9gUVYWTRJ+YQ==
Connection: keep-alive, Upgrade
Cookie: _gcl_au=1.1.1222441835.1654180959; _ga=GA1.2.1857463516.1654180960; _gid=GA1.2.265480995.1654180960; _fbp=fb.1.1654180960477.1170827820
Sec-Fetch-Dest: websocket
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: cross-site
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Example CSWSH Response
HTTP/1.1 101 Switching Protocols
Date: Thu, 02 Jun 2022 14:46:12 GMT
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: KsM7mIwWmDR0JLRLdXQ3TtvS18E=
X-Powered-By: Ratchet/0.4.4
Testing¶
Web Socket Tester:
<html>
<head>
<title>WebSocket Tester</title>
<script language="javascript" type="text/javascript">
var websocket;
var ping;
//Setup
var connect = document.getElementById('connect')
connect.addEventListener("click", doConnect);
var disconnect = document.getElementById('disconnect')
disconnect.addEventListener("click", doDisconnect);
var send = document.getElementById('send').addEventListener("click", function () {
doSend( $('#message').val() )
});
//Callback Functions
function doConnect() {
websocket = new WebSocket( document.getElementById("target").val() );
websocket.onopen = function (evt) {
onOpen(evt)
var ping = setInterval(function () { doPing() }, 1000);
};
websocket.onclose = function (evt) {
onClose(evt)
};
websocket.onmessage = function (evt) {
onMessage(evt)
};
websocket.onerror = function (evt) {
onError(evt)
};
}
function doDisconnect() {
websocket.close();
}
function onOpen(evt) {
writeToScreen("CONNECTED");
}
function onClose(evt) {
writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
if ((evt.data != "ping") && (evt.data != "pong")) {
writeToScreen('RECIEVED: ' + evt.data);
}
}
function onError(evt) {
writeToScreen('ERROR:' + evt.data);
}
function doSend(message) {
if (message != "ping") {
writeToScreen('SENT: ' + message);
}
websocket.send(message);
}
function writeToScreen(message) {
var output = document.getElementById("output")
output.append(message + '<br /><br />');
}
function doPing() {
if (websocket != "undefined") {
doSend("ping");
}
}
</script>
</head>
<body>
<h2>WebSocket Tester</h2>
Target:
<input type="text" id="target" value="" />
<br />
<button id="connect">Connect</button>
<button id="disconnect">Disconnect</button>
<br />
<br />Message:
<input type="text" id="message" value="" />
<button id="send">Send</button>
<br />
<br />Output:
<br /> <pre><div id="output"></div></pre>
</body>
</html>
Testing Script:
python3 ws_tester.py -u wss://example.com -m unauthed-ws.txt -w IFUZZ:./SecLists/Fuzzing/numeric-fields-only.txt -w SFUZZ:./SecLists/Fuzzing/fuzz-Bo0oM.txt -s COUNT:count
Testing Script2:
python3 ws_tester.py -u wss://example.com -m unauthed-ws.txt -w IFUZZ:/./SecLists/Fuzzing/numeric-fields-only.txt -w SFUZZ:./SecLists/Fuzzing/fuzz-Bo0oM.txt -s COUNT:count -s 87331041:checksum -s TxxxxxxxxxOxxxxxxxxxxKxxxxxxxExxxxxxxxN:token