Friday, March 4, 2011

HTML5 WebSockets - security & new tool for attacking

WebSockets is definately one of the brighter features of HTML5. It allows for easy and efficient real-time commucation with the server, and with the introduction of Socket.IO, node.js  and similar libraries, it is sure to gain popularity. It's a must when you're developing an interactive application like chat, game, realtime reporting system etc.

But, from a security standpoint there are many things to consider when implementing WebSockets in your next project. I don't call them vulnerabilities - but they will most likely create a vulnerability when not dealt with correctly. In this post I describe all these aspects and release socket_io_client - tool for testing & exploiting WebSockets servers.

Auth & auth

If the resources are not public, you need to handle authentication & authorization by yourself - the protocol gives you nothing by default (apart from cookies & HTTP Auth, which have some issues themselves).

Don't trust the client

Server cannot blindly trust the client. Your application-level protocol should be bullet-proof as the client can be hijacked and/or spoofed. It's best to assume that your protocol will be fuzzed by someone in the future. You need to handle:
  • data format issues 
  • charset & encoding issues
  • linebreak issues
  • logging
  • denial of service attacks
and make extensive input validation in the server. Basically, you need to write the Apache equivalent for WebSockets application or trust that someone did it correctly. Good luck!

For example: Socket.IO, the most popular WebSockets server I encountered, in it's short history has used at least 2 incompatible message formats and 2 months ago it could be crashed with trivial payload (it's now fixed). And you need to write an application for Socket.IO with your own invented protocol.

It's beta time!

WebSockets protocol is evolving - handshake 76 is currently used in browsers, but the standard will change in the next few months - browsers and WebSocket servers need to keep up.

As an example, Socket.IO still handles old handshake protocol for backwards compatibility. While this is understandable from user's perspective, basically it's a rarely tested branch of code that allows attackers to skip some of the security checks introduced in handshake 76 for a reason.

Plaintext

The communications in ws:// protocol in handled in plaintext, so consider using only it's encrypted wss:// version. We don't need another Firesheep in two years. With SSL, you protect the clients from connecting to a spoofed server and you're not giving the plaintext version of your conversation to anyone listening.

Check the Origin

Unless you want to speak with the whole world, validate the Origin of your clients.

By default, Socket.IO allows all origins, which means that you could establish a connection from any domain. Use origins config parameter to narrow that.

XSS can hijack the client

If the client website has a XSS flaw, it has access to your connection and it can:
  • log all messages, both sent and received
  • DoS the server
  • send arbitrary messages

It's not only browsers

What is important - don't assume the WebSockets client is a browser, with it's Same Origin Policy, cookies, Javascript engine, Origin headers etc. It can be anything, especially if the attacker is determined. Don't take my word for it and check for yourself - I wrote a simple malicious Socket.IO client in Python. It can:
  • Handshake with a Socket.IO server
  • Ignore all Origin restrictions
  • Transparently handle all socket.io heartbeats
  • Send arbitrary messages - from a prompt or an input file
  • Messages could be raw or properly formatted according to socket.io protocol
  • Receive/log all server messages
I also included a few exemplary payloads which can crash servers I encountered. You can test the client against my vulnerable chat application (try XSS).
  1. Connect (with Chrome or other browser supporting websockets) to http://vuln.nodester.com/chat.html
  2. Run the command line client
    ./socket_io_client.py vuln.nodester.com 80
  3. Start conversation
  4. Try to inject XSS from the command line client

You could also use my prepared payloads like so:
./socket_io_client.py vuln.nodester.com 80 < payloads.txt
Or save all server reponses like so:
./socket_io_client.py vuln.nodester.com 80 > output.txt

Just watch this Youtube video for a quick demo:



Socket_io_client is freely downloadable from github.

1 comment:

Stephan said...

Nice post! The best point in my oppinion is, that you point out, that the developer must assume, that the protocol will be fuzzed in the future and hence always validate the client input.

Thankyou for writing this!