Martin Suchan – BloQ Random #WPdev stuff

3Nov/160

Hacking UWP WebView Part 3. Bypassing HTTPS certificate validation in UWP WebView

As described in my previous article Hacking UWP WebView Part 1. Displaying HTTPS page with invalid certificate in UWP WebView, there is a limited way how to display HTTPS web page with invalid certificate in the UWP WebView. It requires using fake IUriToStreamResolver for making proxy requests to the target web with modified HttpClient that is ignoring certificate errors.
I've even added the possibility to easily load pages with certificate errors to my WebView wishlist of missing features in my article Make WebView great again!.

Luckily, while developing Seznam.cz app for Windows 10 Mobile (Czech only), currently the most advanced browser available on Windows Store, I've found another way how to display web page with invalid domain certificate in a WebView. The problem with this solution is that it looks more like a vulnerability or oversight, because the behavior described below is not documented anywhere.

How it works

First some theory, when navigating in WebView to web site with invalid domain certificate, the NavigationFailed event is raised with WebErrorStatus equal to CertificateIsInvalid. This is the expected behavior for webs with invalid certificates.

To change this behavior we need to create HttpBaseProtocolFilter instance and set it in a way to ignore all ignorable certificate validation errors like this:

HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.IncompleteChain);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.WrongUsage);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationInformationMissing);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationFailure);

Then we'll create HttpClient instance with this filter and make Head request to the target site that has invalid certificate.

Uri uri = new Uri("https://expired.badssl.com/");
HttpClient hc = new HttpClient(filter);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Head, uri);
HttpResponseMessage response = await hc.SendRequestAsync(request);

This request most likely succeeds with HTTP Status Code 200. That's because the HttpBaseProtocolFilter ignores the error we added to the IgnorableServerCertificateErrors list.

Up until now this is all expected behavior of the HttpBaseProtocolFilter and HttpClient class.

What is NOT expected - when you now try to navigate to the same web with invalid certificate inside a WebView now, it works!

Wait, what just happened?

Good question, I have no idea 🙂
Actually I have a hunch that both WebView and HttpBaseProtocolFilter shares the same network stack and somehow when doing request with HttpClient with filter configured to ignore certificate errors, the network stack caches the information about the target domain to ignore certificate errors from now on.
I've done some research to pinpoint the actual behavior and here is the result:

  • It works like described on Windows 10 Anniversary Update, both Desktop and Mobile, with latest cummulative update (November 2016).
  • It's necessary to ignore the specific certificate error type for the target domain when doing the HttpClient request with HttpBaseProtocolFilter.
  • The navigation with invalid certificate in WebView then works only for the specific domain.
  • Requests to the target domain then work even in new instance of HttpClient with new, empty HttpBaseProtocolFilter.
  • I was not able to make the target domain un-ignore certificate errors for the rest of app lifetime - all requests to the target web work as long as the app live.

Conclusion

The unforseen consequences in this behavior might be dire - just by making request to target domain with ignoring HttpBaseProtocolFilter in one part of the app, completely different part of the app might start trusting expired or invalid certificates in a WebView. On one hand we discovered, how to easily load page with invalid certificate in the WebView, but on other hand this could compromise the overall security of our app.

There are also some unanswered questions - what happens when using ignoring HttpBaseProtocolFilter in app contracts, calling one app from another?
What happens If I try to use ignoring HttpBaseProtocolFilter in App Extension?
What happens if I create extension for Edge and I use this method on www.microsoft.com, does it mean it could lead to easy way how to MITM any site? Or there are separate network stacks for each app including extensions?
I plan to update this article once I find answers to these questions. For now you can discuss this issue with me on Twitter.

28Apr/168

Make WebView great again!

When developing Universal Windows Platform (UWP) apps for Windows 10 one of the core elements developers often use is the WebView. It's a control designed for displaying Web Pages, either from local or any online source. It's basically a Frame for rendering HTML content powered by the EdgeHTML rendering core.

The WebView went through many iterations since its conception in Windows 8 in late 2012. It went from hard to use always-on-top element powered by IE10 Trident Core to a modern and quite capable control supporting current HTML5 standards and modern HTTPS cipher suites.

From a developers' point of view lot of useful methods and settings was added in the Windows 10 version of WebView, for instance event handlers to allow access to Geolocation API or Webcam, detection when the website wants to open a new window, notification about loading state od each Frame, possibility to provide native object which is then available from the page DOM, and much more.

Unfortunately, the WebView, as it's available today, is still incomplete in terms of usability and does not provide several critical APIs. In this article I have gathered the most important missing or incomplete WebView features that would make the WebView finally a first class citizen, or great again.

I sorted these features into three groups:

  • Must fix are critical security and/or functional features that cannot be implemented using any workaround
  • Should fix are features, that would make the WebView much easier to use, but are not critical for daily use or can be implemented using nontrivial workarounds, like JavaScript injection and notification back to the app
  • Nice to haves are optional improvements for making developer more happy 🙂

Note that all missing features requested below are already available in the Microsoft Edge browser. I’m not requesting something, that is not yet available in the EdgeHTML core for obvious reasons.

14Jan/164

Hacking UWP WebView Part 2. Bypassing window.external.notify whitelist

This is a second part of my small series of blogposts about unlocking hidden features of Windows 10 UWP WebView. You can read the first part about Displaying HTTPS page with invalid certificate in UWP WebView here.

When using WebView in your Windows Store app you often need to communicate between your C# app and the code inside the WebView. For executing JavaScript code in WebView from C# it's easy to use the WebView.InvokeScriptAsync method. This asynchronous action executes the specified script function from the currently loaded HTML with specific arguments. This method works on any page loaded in WebView.

But the problem arises when you want the JavaScript code in WebView to notify your C# code. By default the JavaScript should call the window.external.notify method with string parameter and this call then raises ScriptNotify event on the WebView. This call works fine when used on pages loaded from application resources, when using NavigateToString or when using NavigateToLocalStreamUri.

The problem

Unfortunately window.external.notify does not work when used in web pages loaded from Local storage using ms-appdata protocol. According to Microsoft this is by design "This was a policy decision we made". I have no idea why this decision was made since it has no effect in terms of security, only makes development harder if I want to use ms-appdata Uri as WebView Source.

Lastly window.external.notify can be used in pages loaded from the Internet using standard HTTPS protocol, BUT it's necessary to explicitly whitelist target domains in package.appxmanifest

Capture

In this whitelist editor it's not possible to use HTTP addresses at all or generic wild-card to "enable all HTTPS websites".
You can imagine that this is quite a big limitation. Let's say you want to create your own browser that will be injecting JavaScript into all pages to find the used favicon - well, that's not possible using the window.external.notify event.

The solution

Luckily there is a solution when developing apps for Windows 10 UWP: the WebView.AddWebAllowedObject method.

In Windows 10 it's possible to create native class in C#, VB or C++ that can be passed to the WebView object and which behaves like a native JavaScript object. This class must be created in another Project of type Windows Runtime Component and marked with [AllowForWeb] attribute. JavaScript code then can call any method on this object and the actual code of this method runs in the C#/VB/C++ implementation.

//KeyHandler.cs
[AllowForWeb]
public sealed class KeyHandler
{
    public void setKeyCombination(int keyPress)
    {
        Debug.WriteLine("Called from WebView! {0}", keyPress);
    }
}
 
// MainPage.xaml.cs
private void Web_OnNavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
    // WebView native object must be inserted in the OnNavigationStarting event handler
    KeyHandler winRTObject = new KeyHandler();
    // Expose the native WinRT object on the page's global object
    Web.AddWebAllowedObject("NotifyApp", winRTObject);
}
 
private async void Web_OnDOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
{
    try
    {
        // inject event handler to arbitrary page once the DOM is loaded
        // in this case add event handlet to click on the main <table> element
        await Web.InvokeScriptAsync("eval",
            new[] { "document.getElementById(\"hnmain\").addEventListener(\"click\", function () { NotifyApp.setKeyCombination(43); }); "});
    }
    catch (Exception e)
    {
        Debug.WriteLine(e);
    }
}

And the best part? This native object can be passed and used in WebView with any page, completely ignoring the Content URIs domain whitelist in Package.appxmanifest.
This is a really good news for developers since they are no longer limited by artificial rules in the app manifest. But on the other hand it's still necessary to carefully evaluate all calls from the native object. Keep in mind that any code on the page can access the native object as well and send malicious values to the app .

I've created simple sample where I load into my WebView the Hacker News web, inject KeyHandler native object and attach event handler when clicking the hnmain element that calls back method on the KeyHandler object back to the C# app.

Summary

It's possible to use notification between C# and JavaScript code in both ways on any page loaded into WebView using the WebView.AddWebAllowedObject method. It's quite easy to use it but keep in mind that with great power comes great responsibility.