Synopsis

XSS Attacks are the second category of the three largest web attacks used today. Here, we’ll set up a node server to demonstrate an XSS attack, see browser based XSS prevention, and finally discuss what further exploits exist based on this attack.

Setup

Here’s our normal, tiny node server to demonstrate XSS.

Create the file server.js as follows:

 var http = require('http'); 
 var url = require('url');
 
 http.createServer(function (req, res) { 
     const query = url.parse(req.url,true).query; 
     res.writeHead(200, { 'Content-Type': 'text/html' }); 
     res.end(''+query.data+''); 
 }).listen(4000);

After starting our server with

 $ node server.js

We can visit our server from our web browser, at the following URL:

 http://localhost:4000/?data=foobar

which yields the following in our web browser:

 foobar

Not very exciting so far, but we have a complete, though basic echo server. Imagine this functionality being used in a profile page to display information a user sends to the web server. This is a common functionality for sites like Facebook and Craigslist.

Browser XSS Protection

What if we include a javascript payload in our request, instead of foobar? Visit the following URL:

http://localhost:4000/?data=alert(window)

instead of seeing an alert popup, we see nothing. In the javascript console, however, we see a variant of the following message (i.e. in Chrome):

The XSS Auditor refused to execute a script in 'http://localhost:4000/?data=%3Cscript%3Ealert(window)%3C/script%3E' because its source code was found within the request. The auditor was enabled as the server sent neither an 'X-XSS-Protection' nor 'Content-Security-Policy' header.<br></br>

What we’re seeing is a recent, common functionality in browsers that prevents us sending XSS attacks through requests. We could get around this by explicitly sending the X-XSS-Protection header with a value indicating the intent to bypass the browser’s XSS protection. This would require us to have access to the server we’re attacking, so we’ll opt for another strategy.

Exploiting HTTP Request Encoding

A common strategy to give a website an air of security is to encode pesky request parameters, or to otherwise weakly obfuscate client/server communication by base64 encoding communications. While this isn’t a robust security mechanism, it’s found out in the wild and provides a vulnerability we can exploit to get around browser XSS protection.

Let’s implement this feature on our web server, changing our res.end line to:

res.end(''+new Buffer(query.data, 'base64').toString("ascii")+'');

Now if we take our javascript payload

 <script>alert(window)</script>

and base64 encode it we get:

 $ echo -n "<script>alert(window)</script>" | base64
 PHNjcmlwdD5hbGVydCh3aW5kb3cpPC9zY3JpcHQ+

If we visit our new URL:

 http://localhost:4000/?data=PHNjcmlwdD5hbGVydCh3aW5kb3cpPC9zY3JpcHQ+

we see our alert does appear.

Further Exploits

This is a small proof-of-concept demonstration of the capabilities of XSS attacks. The general ability granted by XSS is to ‘inject’ arbitrary HTML, javascript (and even CSS!) into a webpage rendered by the browser. This allows for:

  • Disclosure of insecure cookies
  • Disclosure of sensitive website content
  • Avenues for other attacks

This code injection acts with the privileges of the scripts executed by the user’s browser, enabling us to control it. This, in our example, is used to control our own browser, disappointingly. XSS attacks are most effective when targeting users visiting a website that displays user-created content, especially those allowing markup, styling or other more advanced controls. These pages, especially those visible publicly, can then be used to control the browser of any visitor to a website containing the malicious user content. This is the true power of the attack, and has hit some very big names like MySpace back in the day, and more recently Facebook. Google has even ‘awarded researchers over $1.2 million for reporting XSS bugs‘ alone.

More Reading & Other Resources